diff --git a/BUILD.gn b/BUILD.gn index 682c24b70b..3b2e844638 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1678,6 +1678,7 @@ v8_source_set("v8_base") { "src/identity-map.h", "src/interface-descriptors.cc", "src/interface-descriptors.h", + "src/interpreter/block-coverage-builder.h", "src/interpreter/bytecode-array-accessor.cc", "src/interpreter/bytecode-array-accessor.h", "src/interpreter/bytecode-array-builder.cc", diff --git a/src/ast/ast.h b/src/ast/ast.h index c899a89cf4..95a6b1a32a 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -182,8 +182,13 @@ class AstProperties final BASE_EMBEDDED { DEFINE_OPERATORS_FOR_FLAGS(AstProperties::Flags) struct SourceRange { - SourceRange() : start(kNoSourcePosition), end(kNoSourcePosition) {} + SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {} + SourceRange(int start, int end) : start(start), end(end) {} bool IsEmpty() const { return start == kNoSourcePosition; } + static SourceRange ContinuationOf(const SourceRange& that) { + return that.IsEmpty() ? SourceRange() + : SourceRange(that.end + 1, kNoSourcePosition); + } int32_t start, end; }; @@ -495,6 +500,9 @@ class IterationStatement : public BreakableStatement { void set_body(Statement* s) { body_ = s; } SourceRange body_range() const { return body_range_; } + SourceRange continuation_range() const { + return SourceRange::ContinuationOf(body_range_); + } int suspend_count() const { return suspend_count_; } int first_suspend_id() const { return first_suspend_id_; } @@ -922,6 +930,11 @@ class IfStatement final : public Statement { SourceRange then_range() const { return then_range_; } SourceRange else_range() const { return else_range_; } + SourceRange continuation_range() const { + SourceRange trailing_range = + HasElseStatement() ? else_range() : then_range(); + return SourceRange::ContinuationOf(trailing_range); + } void set_condition(Expression* e) { condition_ = e; } void set_then_statement(Statement* s) { then_statement_ = s; } diff --git a/src/debug/debug-coverage.cc b/src/debug/debug-coverage.cc index 91b1059398..a213d98703 100644 --- a/src/debug/debug-coverage.cc +++ b/src/debug/debug-coverage.cc @@ -4,6 +4,7 @@ #include "src/debug/debug-coverage.h" +#include "src/ast/ast.h" #include "src/base/hashmap.h" #include "src/deoptimizer.h" #include "src/frames-inl.h" @@ -59,8 +60,8 @@ bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) { } bool CompareCoverageBlock(const CoverageBlock& a, const CoverageBlock& b) { - DCHECK(a.start != kNoSourcePosition && a.end != kNoSourcePosition); - DCHECK(b.start != kNoSourcePosition && b.end != kNoSourcePosition); + DCHECK(a.start != kNoSourcePosition); + DCHECK(b.start != kNoSourcePosition); if (a.start == b.start) return a.end > b.end; return a.start < b.start; } @@ -81,8 +82,6 @@ std::vector GetSortedBlockData(Isolate* isolate, const int count = coverage_info->BlockCount(i); DCHECK(start_pos != kNoSourcePosition); - DCHECK(until_pos != kNoSourcePosition); - result.emplace_back(start_pos, until_pos, count); } @@ -91,6 +90,44 @@ std::vector GetSortedBlockData(Isolate* isolate, return result; } + +// Rewrite position singletons (produced by unconditional control flow +// like return statements, and by continuation counters) into source +// ranges that end at the next sibling range or the end of the parent +// range, whichever comes first. +void RewritePositionSingletonsToRanges(CoverageFunction* function) { + std::vector nesting_stack; + nesting_stack.emplace_back(function->start, function->end); + + const int blocks_count = static_cast(function->blocks.size()); + for (int i = 0; i < blocks_count; i++) { + CoverageBlock& block = function->blocks[i]; + + while (nesting_stack.back().end < block.start) { + nesting_stack.pop_back(); + } + + const SourceRange& parent_range = nesting_stack.back(); + + DCHECK(block.start != kNoSourcePosition); + if (block.end == kNoSourcePosition) { + // The current block ends at the next sibling block (if it exists) or the + // end of the parent block otherwise. + if (i < blocks_count - 1 && + function->blocks[i + 1].start <= parent_range.end) { + block.end = function->blocks[i + 1].start - 1; + } else { + block.end = parent_range.end; + } + } + + if (i < blocks_count - 1) { + nesting_stack.emplace_back(block.start, block.end); + } + } + + DCHECK_EQ(1, nesting_stack.size()); +} } // anonymous namespace Coverage* Coverage::CollectPrecise(Isolate* isolate) { @@ -207,6 +244,7 @@ Coverage* Coverage::Collect(Isolate* isolate, if (FLAG_block_coverage && info->HasCoverageInfo()) { CoverageFunction* function = &functions->back(); function->blocks = GetSortedBlockData(isolate, info); + RewritePositionSingletonsToRanges(function); } } } diff --git a/src/interpreter/block-coverage-builder.h b/src/interpreter/block-coverage-builder.h new file mode 100644 index 0000000000..da2ee06e84 --- /dev/null +++ b/src/interpreter/block-coverage-builder.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_ +#define V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_ + +#include "src/ast/ast.h" +#include "src/interpreter/bytecode-array-builder.h" + +#include "src/zone/zone-containers.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +// Used to generate IncBlockCounter bytecodes and the {source range, slot} +// mapping for block coverage. +class BlockCoverageBuilder final : public ZoneObject { + public: + BlockCoverageBuilder(Zone* zone, BytecodeArrayBuilder* builder) + : slots_(0, zone), builder_(builder) {} + + static const int kNoCoverageArraySlot = -1; + + int AllocateBlockCoverageSlot(SourceRange range) { + if (range.IsEmpty()) return kNoCoverageArraySlot; + const int slot = static_cast(slots_.size()); + slots_.emplace_back(range); + return slot; + } + + void IncrementBlockCounter(int coverage_array_slot) { + if (coverage_array_slot == kNoCoverageArraySlot) return; + builder_->IncBlockCounter(coverage_array_slot); + } + + const ZoneVector& slots() const { return slots_; } + + private: + // Contains source range information for allocated block coverage counter + // slots. Slot i covers range slots_[i]. + ZoneVector slots_; + BytecodeArrayBuilder* builder_; +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_ diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 706d17b65e..de009eb7e1 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -737,36 +737,6 @@ class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject { bool has_constant_pool_entry_; }; -// Used to generate IncBlockCounter bytecodes and the {source range, slot} -// mapping for block coverage. -class BytecodeGenerator::BlockCoverageBuilder final : public ZoneObject { - public: - explicit BlockCoverageBuilder(Zone* zone, BytecodeArrayBuilder* builder) - : slots_(0, zone), builder_(builder) {} - - static const int kNoCoverageArraySlot = -1; - - int AllocateBlockCoverageSlot(SourceRange range) { - if (range.IsEmpty()) return kNoCoverageArraySlot; - const int slot = static_cast(slots_.size()); - slots_.emplace_back(range); - return slot; - } - - void IncrementBlockCounter(int coverage_array_slot) { - if (coverage_array_slot == kNoCoverageArraySlot) return; - builder_->IncBlockCounter(coverage_array_slot); - } - - const ZoneVector& slots() const { return slots_; } - - private: - // Contains source range information for allocated block coverage counter - // slots. Slot i covers range slots_[i]. - ZoneVector slots_; - BytecodeArrayBuilder* builder_; -}; - class BytecodeGenerator::CurrentScope final { public: CurrentScope(BytecodeGenerator* generator, Scope* scope) @@ -1237,6 +1207,8 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { int then_slot = AllocateBlockCoverageSlotIfEnabled(stmt->then_range()); int else_slot = AllocateBlockCoverageSlotIfEnabled(stmt->else_range()); + int continuation_slot = + AllocateBlockCoverageSlotIfEnabled(stmt->continuation_range()); if (stmt->condition()->ToBooleanIsTrue()) { // Generate then block unconditionally as always true. @@ -1271,6 +1243,7 @@ void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { } builder()->Bind(&end_label); } + BuildIncrementBlockCoverageCounterIfEnabled(continuation_slot); } void BytecodeGenerator::VisitSloppyBlockFunctionStatement( @@ -1361,6 +1334,7 @@ void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt, LoopBuilder* loop_builder) { + loop_builder->LoopBody(); ControlScopeForIteration execution_control(this, stmt, loop_builder); builder()->StackCheck(stmt->position()); Visit(stmt->body()); @@ -1368,20 +1342,16 @@ void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt, } void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { - int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); - - LoopBuilder loop_builder(builder()); + LoopBuilder loop_builder(builder(), block_coverage_builder_, + stmt->body_range(), stmt->continuation_range()); if (stmt->cond()->ToBooleanIsFalse()) { - BuildIncrementBlockCoverageCounterIfEnabled(body_slot); VisitIterationBody(stmt, &loop_builder); } else if (stmt->cond()->ToBooleanIsTrue()) { VisitIterationHeader(stmt, &loop_builder); - BuildIncrementBlockCoverageCounterIfEnabled(body_slot); VisitIterationBody(stmt, &loop_builder); loop_builder.JumpToHeader(loop_depth_); } else { VisitIterationHeader(stmt, &loop_builder); - BuildIncrementBlockCoverageCounterIfEnabled(body_slot); VisitIterationBody(stmt, &loop_builder); builder()->SetExpressionAsStatementPosition(stmt->cond()); BytecodeLabels loop_backbranch(zone()); @@ -1393,14 +1363,14 @@ void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { } void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { - int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); + LoopBuilder loop_builder(builder(), block_coverage_builder_, + stmt->body_range(), stmt->continuation_range()); if (stmt->cond()->ToBooleanIsFalse()) { // If the condition is false there is no need to generate the loop. return; } - LoopBuilder loop_builder(builder()); VisitIterationHeader(stmt, &loop_builder); if (!stmt->cond()->ToBooleanIsTrue()) { builder()->SetExpressionAsStatementPosition(stmt->cond()); @@ -1409,13 +1379,13 @@ void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) { TestFallthrough::kThen); loop_body.Bind(builder()); } - BuildIncrementBlockCoverageCounterIfEnabled(body_slot); VisitIterationBody(stmt, &loop_builder); loop_builder.JumpToHeader(loop_depth_); } void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { - int body_slot = AllocateBlockCoverageSlotIfEnabled(stmt->body_range()); + LoopBuilder loop_builder(builder(), block_coverage_builder_, + stmt->body_range(), stmt->continuation_range()); if (stmt->init() != nullptr) { Visit(stmt->init()); @@ -1426,7 +1396,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { return; } - LoopBuilder loop_builder(builder()); VisitIterationHeader(stmt, &loop_builder); if (stmt->cond() && !stmt->cond()->ToBooleanIsTrue()) { builder()->SetExpressionAsStatementPosition(stmt->cond()); @@ -1435,7 +1404,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) { TestFallthrough::kThen); loop_body.Bind(builder()); } - BuildIncrementBlockCoverageCounterIfEnabled(body_slot); VisitIterationBody(stmt, &loop_builder); if (stmt->next() != nullptr) { builder()->SetStatementPosition(stmt->next()); diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index 8b44f3acc8..ab498d8469 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -21,6 +21,7 @@ namespace interpreter { class GlobalDeclarationsBuilder; class LoopBuilder; +class BlockCoverageBuilder; class BytecodeJumpTable; class BytecodeGenerator final : public AstVisitor { @@ -51,7 +52,6 @@ class BytecodeGenerator final : public AstVisitor { class ExpressionResultScope; class EffectResultScope; class GlobalDeclarationsBuilder; - class BlockCoverageBuilder; class RegisterAllocationScope; class TestResultScope; class ValueResultScope; diff --git a/src/interpreter/control-flow-builders.cc b/src/interpreter/control-flow-builders.cc index e4281667c2..557afedab4 100644 --- a/src/interpreter/control-flow-builders.cc +++ b/src/interpreter/control-flow-builders.cc @@ -52,6 +52,11 @@ LoopBuilder::~LoopBuilder() { if (generator_jump_table_location_ != nullptr) { *generator_jump_table_location_ = parent_generator_jump_table_; } + // Generate block coverage counter for the continuation. + if (block_coverage_builder_ != nullptr) { + block_coverage_builder_->IncrementBlockCounter( + block_coverage_continuation_slot_); + } } void LoopBuilder::LoopHeader() { @@ -83,6 +88,12 @@ void LoopBuilder::LoopHeaderInGenerator( builder()->AllocateJumpTable(resume_count, first_resume_id); } +void LoopBuilder::LoopBody() { + if (block_coverage_builder_ != nullptr) { + block_coverage_builder_->IncrementBlockCounter(block_coverage_body_slot_); + } +} + void LoopBuilder::JumpToHeader(int loop_depth) { // Pass the proper loop nesting level to the backwards branch, to trigger // on-stack replacement when armed for the given loop nesting depth. diff --git a/src/interpreter/control-flow-builders.h b/src/interpreter/control-flow-builders.h index 8cff017e78..6275450647 100644 --- a/src/interpreter/control-flow-builders.h +++ b/src/interpreter/control-flow-builders.h @@ -7,6 +7,7 @@ #include "src/interpreter/bytecode-array-builder.h" +#include "src/interpreter/block-coverage-builder.h" #include "src/interpreter/bytecode-label.h" #include "src/zone/zone-containers.h" @@ -87,16 +88,29 @@ class V8_EXPORT_PRIVATE BlockBuilder final // their loop. class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { public: - explicit LoopBuilder(BytecodeArrayBuilder* builder) + LoopBuilder(BytecodeArrayBuilder* builder, + BlockCoverageBuilder* block_coverage_builder = nullptr, + const SourceRange& body_range = {}, + const SourceRange& continuation_range = {}) : BreakableControlFlowBuilder(builder), continue_labels_(builder->zone()), generator_jump_table_location_(nullptr), - parent_generator_jump_table_(nullptr) {} + parent_generator_jump_table_(nullptr), + block_coverage_builder_(block_coverage_builder) { + if (block_coverage_builder_ != nullptr) { + block_coverage_body_slot_ = + block_coverage_builder_->AllocateBlockCoverageSlot(body_range); + block_coverage_continuation_slot_ = + block_coverage_builder_->AllocateBlockCoverageSlot( + continuation_range); + } + } ~LoopBuilder(); void LoopHeader(); void LoopHeaderInGenerator(BytecodeJumpTable** parent_generator_jump_table, int first_resume_id, int resume_count); + void LoopBody(); void JumpToHeader(int loop_depth); void BindContinueTarget(); @@ -120,6 +134,10 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder { // field is ugly, figure out a better way to do this. BytecodeJumpTable** generator_jump_table_location_; BytecodeJumpTable* parent_generator_jump_table_; + + int block_coverage_body_slot_; + int block_coverage_continuation_slot_; + BlockCoverageBuilder* block_coverage_builder_; }; diff --git a/src/v8.gyp b/src/v8.gyp index 3a03893910..ab08b9151b 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -1135,6 +1135,7 @@ 'identity-map.h', 'interface-descriptors.cc', 'interface-descriptors.h', + 'interpreter/block-coverage-builder.h', 'interpreter/bytecodes.cc', 'interpreter/bytecodes.h', 'interpreter/bytecode-array-accessor.cc', diff --git a/test/mjsunit/code-coverage-block.js b/test/mjsunit/code-coverage-block.js index cede101f39..20391943ea 100644 --- a/test/mjsunit/code-coverage-block.js +++ b/test/mjsunit/code-coverage-block.js @@ -21,7 +21,7 @@ function TestCoverage(name, source, expectation) { var stringified_result = JSON.stringify(covfefe); var stringified_expectation = JSON.stringify(expectation); if (stringified_result != stringified_expectation) { - print(JSON.stringify(covfefe, undefined, 1)); + print(stringified_result.replace(/[}],[{]/g, "},\n {")); } assertEquals(stringified_expectation, stringified_result, name + " failed"); } @@ -66,16 +66,44 @@ f(43); {"start":45,"end":83,"count":1}, {"start":64,"end":69,"count":0}, {"start":71,"end":79,"count":1}, + {"start":80,"end":83,"count":1}, + {"start":84,"end":97,"count":2}, {"start":98,"end":107,"count":1}, {"start":109,"end":121,"count":1}, + {"start":122,"end":135,"count":2}, {"start":136,"end":141,"count":1}, {"start":143,"end":151,"count":1}, + {"start":152,"end":163,"count":2}, {"start":164,"end":169,"count":0}, {"start":171,"end":179,"count":2}, + {"start":180,"end":191,"count":2}, {"start":192,"end":197,"count":0}, + {"start":198,"end":208,"count":2}, {"start":209,"end":214,"count":2}, {"start":216,"end":224,"count":0}, - {"start":236,"end":241,"count":2}] + {"start":225,"end":235,"count":2}, + {"start":236,"end":241,"count":2}, + {"start":242,"end":244,"count":2}] +); + +function nop() {} + +TestCoverage( +"if statement (early return)", +` +!function() { // 0000 + if (true) { // 0050 + nop(); // 0100 + return; // 0150 + nop(); // 0200 + } // 0250 + nop(); // 0300 +}() // 0350 +`, +[{"start":0,"end":399,"count":1}, + {"start":1,"end":351,"count":1}, + {"start":60,"end":252,"count":1}, + {"start":253,"end":351,"count":0}] ); TestCoverage( @@ -98,12 +126,18 @@ function g() {} {"start":0,"end":15,"count":36}, {"start":17,"end":256,"count":1}, {"start":59,"end":64,"count":12}, + {"start":65,"end":94,"count":1}, {"start":95,"end":110,"count":12}, + {"start":111,"end":139,"count":1}, {"start":140,"end":145,"count":0}, + {"start":146,"end":173,"count":1}, {"start":174,"end":181,"count":1}, + {"start":182,"end":211,"count":1}, {"start":212,"end":253,"count":12}, {"start":234,"end":239,"count":4}, - {"start":241,"end":249,"count":8}] + {"start":241,"end":249,"count":8}, + {"start":250,"end":253,"count":12}, + {"start":254,"end":256,"count":1}] ); TestCoverage( @@ -122,9 +156,47 @@ function g() {} {"start":0,"end":15,"count":36}, {"start":17,"end":168,"count":1}, {"start":72,"end":77,"count":12}, + {"start":78,"end":109,"count":1}, {"start":110,"end":115,"count":12}, + {"start":116,"end":141,"count":1}, {"start":142,"end":147,"count":12}, - {"start":158,"end":165,"count":1}] + {"start":148,"end":157,"count":1}, + {"start":158,"end":165,"count":1}, + {"start":166,"end":168,"count":1}] +); + +TestCoverage( +"for statement (early return)", +` +!function() { // 0000 + for (var i = 0; i < 10; i++) { // 0050 + nop(); // 0100 + continue; // 0150 + nop(); // 0200 + } // 0250 + nop(); // 0300 + for (;;) { // 0350 + nop(); // 0400 + break; // 0450 + nop(); // 0500 + } // 0550 + nop(); // 0600 + for (;;) { // 0650 + nop(); // 0700 + return; // 0750 + nop(); // 0800 + } // 0850 + nop(); // 0900 +}() // 0950 +`, +[{"start":0,"end":999,"count":1}, + {"start":1,"end":951,"count":1}, + {"start":79,"end":252,"count":10}, + {"start":253,"end":358,"count":1}, + {"start":359,"end":552,"count":1}, + {"start":553,"end":658,"count":1}, + {"start":659,"end":852,"count":1}, + {"start":853,"end":951,"count":0}] ); TestCoverage( @@ -148,13 +220,91 @@ function g() {} {"start":0,"end":15,"count":25}, {"start":17,"end":313,"count":1}, {"start":61,"end":66,"count":12}, + {"start":67,"end":89,"count":1}, {"start":90,"end":104,"count":12}, + {"start":105,"end":126,"count":1}, {"start":127,"end":132,"count":0}, + {"start":133,"end":153,"count":1}, {"start":154,"end":161,"count":1}, + {"start":162,"end":172,"count":1}, {"start":173,"end":179,"count":12}, + {"start":180,"end":205,"count":1}, {"start":206,"end":221,"count":12}, + {"start":222,"end":247,"count":1}, {"start":248,"end":258,"count":1}, - {"start":284,"end":296,"count":1}] + {"start":259,"end":283,"count":1}, + {"start":284,"end":296,"count":1}, + {"start":297,"end":313,"count":1}] +); + +TestCoverage( +"while statement (early return)", +` +!function() { // 0000 + let i = 0; // 0050 + while (i < 10) { // 0100 + i++; // 0150 + continue; // 0200 + nop(); // 0250 + } // 0300 + nop(); // 0350 + while (true) { // 0400 + nop(); // 0450 + break; // 0500 + nop(); // 0550 + } // 0600 + nop(); // 0650 + while (true) { // 0700 + nop(); // 0750 + return; // 0800 + nop(); // 0850 + } // 0900 + nop(); // 0950 +}() // 1000 +`, +[{"start":0,"end":1049,"count":1}, + {"start":1,"end":1001,"count":1}, + {"start":115,"end":302,"count":10}, + {"start":303,"end":412,"count":1}, + {"start":413,"end":602,"count":1}, + {"start":603,"end":712,"count":1}, + {"start":713,"end":902,"count":1}, + {"start":903,"end":1001,"count":0}] +); + +TestCoverage( +"do-while statement (early return)", +` +!function() { // 0000 + let i = 0; // 0050 + do { // 0100 + i++; // 0150 + continue; // 0200 + nop(); // 0250 + } while (i < 10); // 0300 + nop(); // 0350 + do { // 0400 + nop(); // 0450 + break; // 0500 + nop(); // 0550 + } while (true); // 0600 + nop(); // 0650 + do { // 0700 + nop(); // 0750 + return; // 0800 + nop(); // 0850 + } while (true); // 0900 + nop(); // 0950 +}() // 1000 +`, +[{"start":0,"end":1049,"count":1}, + {"start":1,"end":1001,"count":1}, + {"start":102,"end":302,"count":10}, + {"start":303,"end":401,"count":1}, + {"start":402,"end":602,"count":1}, + {"start":603,"end":701,"count":1}, + {"start":702,"end":902,"count":1}, + {"start":903,"end":1001,"count":0}] ); %DebugToggleBlockCoverage(false);