From 35c28ce0a742e58346d2dea009428cacd442040d Mon Sep 17 00:00:00 2001 From: yangguo Date: Mon, 20 Jul 2015 07:53:28 -0700 Subject: [PATCH] Debugger: prepare code for debugging on a per-function basis. Prior to this patch, we enter a global debug mode whenever a break point is set. By entering this mode, all code is deoptimized and activated frames are recompiled and redirected to newly compiled debug code. After this patch, we only deoptimize/redirect for functions we want to debug. Trigger for this is Debug::EnsureDebugInfo, and having DebugInfo object attached to the SFI prevents optimization/inlining. The result is that we can have optimized code for functions without break points alongside functions that do have break points, which are not optimized. R=mstarzinger@chromium.org, ulan@chromium.org BUG=v8:4132 LOG=Y Review URL: https://codereview.chromium.org/1233073005 Cr-Commit-Position: refs/heads/master@{#29758} --- src/arm/assembler-arm-inl.h | 6 +- src/arm64/assembler-arm64-inl.h | 6 +- src/background-parsing-task.cc | 3 +- src/bailout-reason.h | 2 +- src/compiler.cc | 123 +++-- src/compiler.h | 36 +- src/compiler/js-inlining.cc | 8 + src/debug.cc | 568 +++++++----------------- src/debug.h | 13 +- src/heap/mark-compact.cc | 5 +- src/hydrogen.cc | 4 + src/ia32/assembler-ia32-inl.h | 6 +- src/ic/ic.cc | 4 +- src/liveedit.cc | 40 +- src/mips/assembler-mips-inl.h | 6 +- src/mips64/assembler-mips64-inl.h | 6 +- src/objects-inl.h | 18 + src/objects.cc | 21 + src/objects.h | 10 + src/runtime/runtime-debug.cc | 63 +-- src/x64/assembler-x64-inl.h | 6 +- test/cctest/test-debug.cc | 2 +- test/cctest/test-func-name-inference.cc | 1 - test/mjsunit/debug-evaluate.js | 1 + test/mjsunit/debug-optimize.js | 54 +++ 25 files changed, 398 insertions(+), 614 deletions(-) create mode 100644 test/mjsunit/debug-optimize.js diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h index 60cda5380..73bf4bb9c 100644 --- a/src/arm/assembler-arm-inl.h +++ b/src/arm/assembler-arm-inl.h @@ -336,8 +336,7 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { } else if (RelocInfo::IsCodeAgeSequence(mode)) { visitor->VisitCodeAgeSequence(this); } else if (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence() && - isolate->debug()->has_break_points()) { + IsPatchedDebugBreakSlotSequence()) { visitor->VisitDebugTarget(this); } else if (RelocInfo::IsRuntimeEntry(mode)) { visitor->VisitRuntimeEntry(this); @@ -360,8 +359,7 @@ void RelocInfo::Visit(Heap* heap) { StaticVisitor::VisitInternalReference(this); } else if (RelocInfo::IsCodeAgeSequence(mode)) { StaticVisitor::VisitCodeAgeSequence(heap, this); - } else if (heap->isolate()->debug()->has_break_points() && - RelocInfo::IsDebugBreakSlot(mode) && + } else if (RelocInfo::IsDebugBreakSlot(mode) && IsPatchedDebugBreakSlotSequence()) { StaticVisitor::VisitDebugTarget(heap, this); } else if (RelocInfo::IsRuntimeEntry(mode)) { diff --git a/src/arm64/assembler-arm64-inl.h b/src/arm64/assembler-arm64-inl.h index ff5c8af0f..729a5d0fd 100644 --- a/src/arm64/assembler-arm64-inl.h +++ b/src/arm64/assembler-arm64-inl.h @@ -884,8 +884,7 @@ void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { } else if (mode == RelocInfo::INTERNAL_REFERENCE) { visitor->VisitInternalReference(this); } else if (RelocInfo::IsDebugBreakSlot(mode) && - IsPatchedDebugBreakSlotSequence() && - isolate->debug()->has_break_points()) { + IsPatchedDebugBreakSlotSequence()) { visitor->VisitDebugTarget(this); } else if (RelocInfo::IsRuntimeEntry(mode)) { visitor->VisitRuntimeEntry(this); @@ -906,8 +905,7 @@ void RelocInfo::Visit(Heap* heap) { StaticVisitor::VisitExternalReference(this); } else if (mode == RelocInfo::INTERNAL_REFERENCE) { StaticVisitor::VisitInternalReference(this); - } else if (heap->isolate()->debug()->has_break_points() && - RelocInfo::IsDebugBreakSlot(mode) && + } else if (RelocInfo::IsDebugBreakSlot(mode) && IsPatchedDebugBreakSlotSequence()) { StaticVisitor::VisitDebugTarget(heap, this); } else if (RelocInfo::IsRuntimeEntry(mode)) { diff --git a/src/background-parsing-task.cc b/src/background-parsing-task.cc index d7df9b471..a79ce5278 100644 --- a/src/background-parsing-task.cc +++ b/src/background-parsing-task.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "src/background-parsing-task.h" +#include "src/debug.h" namespace v8 { namespace internal { @@ -31,7 +32,7 @@ BackgroundParsingTask::BackgroundParsingTask( info->set_global(); info->set_unicode_cache(&source_->unicode_cache); - bool disable_lazy = Compiler::DebuggerWantsEagerCompilation(isolate); + bool disable_lazy = isolate->debug()->RequiresEagerCompilation(); if (disable_lazy && options == ScriptCompiler::kProduceParserCache) { // Producing cached data while parsing eagerly is not supported. options = ScriptCompiler::kNoCompileOptions; diff --git a/src/bailout-reason.h b/src/bailout-reason.h index 13182c0b4..9b547258c 100644 --- a/src/bailout-reason.h +++ b/src/bailout-reason.h @@ -49,7 +49,6 @@ namespace internal { V(kCopyBuffersOverlap, "Copy buffers overlap") \ V(kCouldNotGenerateZero, "Could not generate +0.0") \ V(kCouldNotGenerateNegativeZero, "Could not generate -0.0") \ - V(kDebuggerHasBreakPoints, "Debugger has break points") \ V(kDebuggerStatement, "DebuggerStatement") \ V(kDeclarationInCatchContext, "Declaration in catch context") \ V(kDeclarationInWithContext, "Declaration in with context") \ @@ -85,6 +84,7 @@ namespace internal { "ForInStatement with non-local each variable") \ V(kForOfStatement, "ForOfStatement") \ V(kFrameIsExpectedToBeAligned, "Frame is expected to be aligned") \ + V(kFunctionBeingDebugged, "Function is being debugged") \ V(kFunctionCallsEval, "Function calls eval") \ V(kFunctionWithIllegalRedeclaration, "Function with illegal redeclaration") \ V(kGeneratedCodeIsTooLarge, "Generated code is too large") \ diff --git a/src/compiler.cc b/src/compiler.cc index ab0a620be..fc6760d3f 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -338,11 +338,10 @@ class HOptimizedGraphBuilderWithPositions: public HOptimizedGraphBuilder { OptimizedCompileJob::Status OptimizedCompileJob::CreateGraph() { DCHECK(info()->IsOptimizing()); - DCHECK(!info()->IsCompilingForDebugging()); // Do not use Crankshaft/TurboFan if we need to be able to set break points. - if (isolate()->debug()->has_break_points()) { - return RetryOptimization(kDebuggerHasBreakPoints); + if (info()->shared_info()->HasDebugInfo()) { + return AbortOptimization(kFunctionBeingDebugged); } // Limit the number of times we try to optimize functions. @@ -905,18 +904,6 @@ MaybeHandle Compiler::GetLazyCode(Handle function) { } -MaybeHandle Compiler::GetUnoptimizedCode( - Handle shared) { - DCHECK(!shared->GetIsolate()->has_pending_exception()); - DCHECK(!shared->is_compiled()); - - Zone zone; - ParseInfo parse_info(&zone, shared); - CompilationInfo info(&parse_info); - return GetUnoptimizedCodeCommon(&info); -} - - bool Compiler::EnsureCompiled(Handle function, ClearExceptionFlag flag) { if (function->is_compiled()) return true; @@ -979,45 +966,41 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) { } -// Compile full code for debugging. This code will have debug break slots -// and deoptimization information. Deoptimization information is required -// in case that an optimized version of this function is still activated on -// the stack. It will also make sure that the full code is compiled with -// the same flags as the previous version, that is flags which can change -// the code generated. The current method of mapping from already compiled -// full code without debug break slots to full code with debug break slots -// depends on the generated code is otherwise exactly the same. -// If compilation fails, just keep the existing code. -MaybeHandle Compiler::GetDebugCode(Handle function) { - CompilationInfoWithZone info(function); - Isolate* isolate = info.isolate(); - VMState state(isolate); - - info.MarkAsDebug(); - - DCHECK(!isolate->has_pending_exception()); - Handle old_code(function->shared()->code()); - DCHECK(old_code->kind() == Code::FUNCTION); - DCHECK(!old_code->has_debug_break_slots()); - - info.MarkCompilingForDebugging(); - if (old_code->is_compiled_optimizable()) { - info.EnableDeoptimizationSupport(); - } else { - info.MarkNonOptimizable(); +MaybeHandle CompileForDebugging(CompilationInfo* info) { + info->MarkAsDebug(); + VMState state(info->isolate()); + if (info->shared_info()->is_compiled()) { + if (info->shared_info()->code()->is_compiled_optimizable()) { + info->EnableDeoptimizationSupport(); + } else { + info->MarkNonOptimizable(); + } } - MaybeHandle maybe_new_code = GetUnoptimizedCodeCommon(&info); + MaybeHandle maybe_new_code = GetUnoptimizedCodeCommon(info); Handle new_code; if (!maybe_new_code.ToHandle(&new_code)) { - isolate->clear_pending_exception(); - } else { - DCHECK_EQ(old_code->is_compiled_optimizable(), - new_code->is_compiled_optimizable()); + info->isolate()->clear_pending_exception(); } return maybe_new_code; } +MaybeHandle Compiler::GetDebugCode(Handle function) { + CompilationInfoWithZone info(function); + VMState state(info.isolate()); + return CompileForDebugging(&info); +} + + +MaybeHandle Compiler::GetDebugCode(Handle shared) { + DCHECK(shared->allows_lazy_compilation_without_context()); + Zone zone; + ParseInfo parse_info(&zone, shared); + CompilationInfo info(&parse_info); + return CompileForDebugging(&info); +} + + void Compiler::CompileForLiveEdit(Handle