11cb0ef41Sopenharmony_ci// Copyright 2017 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#include "src/debug/debug-coverage.h" 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci#include "src/ast/ast-source-ranges.h" 81cb0ef41Sopenharmony_ci#include "src/ast/ast.h" 91cb0ef41Sopenharmony_ci#include "src/base/hashmap.h" 101cb0ef41Sopenharmony_ci#include "src/common/assert-scope.h" 111cb0ef41Sopenharmony_ci#include "src/common/globals.h" 121cb0ef41Sopenharmony_ci#include "src/debug/debug.h" 131cb0ef41Sopenharmony_ci#include "src/deoptimizer/deoptimizer.h" 141cb0ef41Sopenharmony_ci#include "src/execution/frames-inl.h" 151cb0ef41Sopenharmony_ci#include "src/execution/isolate.h" 161cb0ef41Sopenharmony_ci#include "src/objects/debug-objects-inl.h" 171cb0ef41Sopenharmony_ci#include "src/objects/objects.h" 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cinamespace v8 { 201cb0ef41Sopenharmony_cinamespace internal { 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ciclass SharedToCounterMap 231cb0ef41Sopenharmony_ci : public base::TemplateHashMapImpl<SharedFunctionInfo, uint32_t, 241cb0ef41Sopenharmony_ci base::KeyEqualityMatcher<Object>, 251cb0ef41Sopenharmony_ci base::DefaultAllocationPolicy> { 261cb0ef41Sopenharmony_ci public: 271cb0ef41Sopenharmony_ci using Entry = base::TemplateHashMapEntry<SharedFunctionInfo, uint32_t>; 281cb0ef41Sopenharmony_ci inline void Add(SharedFunctionInfo key, uint32_t count) { 291cb0ef41Sopenharmony_ci Entry* entry = LookupOrInsert(key, Hash(key), []() { return 0; }); 301cb0ef41Sopenharmony_ci uint32_t old_count = entry->value; 311cb0ef41Sopenharmony_ci if (UINT32_MAX - count < old_count) { 321cb0ef41Sopenharmony_ci entry->value = UINT32_MAX; 331cb0ef41Sopenharmony_ci } else { 341cb0ef41Sopenharmony_ci entry->value = old_count + count; 351cb0ef41Sopenharmony_ci } 361cb0ef41Sopenharmony_ci } 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci inline uint32_t Get(SharedFunctionInfo key) { 391cb0ef41Sopenharmony_ci Entry* entry = Lookup(key, Hash(key)); 401cb0ef41Sopenharmony_ci if (entry == nullptr) return 0; 411cb0ef41Sopenharmony_ci return entry->value; 421cb0ef41Sopenharmony_ci } 431cb0ef41Sopenharmony_ci 441cb0ef41Sopenharmony_ci private: 451cb0ef41Sopenharmony_ci static uint32_t Hash(SharedFunctionInfo key) { 461cb0ef41Sopenharmony_ci return static_cast<uint32_t>(key.ptr()); 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci DISALLOW_GARBAGE_COLLECTION(no_gc) 501cb0ef41Sopenharmony_ci}; 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_cinamespace { 531cb0ef41Sopenharmony_ciint StartPosition(SharedFunctionInfo info) { 541cb0ef41Sopenharmony_ci int start = info.function_token_position(); 551cb0ef41Sopenharmony_ci if (start == kNoSourcePosition) start = info.StartPosition(); 561cb0ef41Sopenharmony_ci return start; 571cb0ef41Sopenharmony_ci} 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_cibool CompareCoverageBlock(const CoverageBlock& a, const CoverageBlock& b) { 601cb0ef41Sopenharmony_ci DCHECK_NE(kNoSourcePosition, a.start); 611cb0ef41Sopenharmony_ci DCHECK_NE(kNoSourcePosition, b.start); 621cb0ef41Sopenharmony_ci if (a.start == b.start) return a.end > b.end; 631cb0ef41Sopenharmony_ci return a.start < b.start; 641cb0ef41Sopenharmony_ci} 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_civoid SortBlockData(std::vector<CoverageBlock>& v) { 671cb0ef41Sopenharmony_ci // Sort according to the block nesting structure. 681cb0ef41Sopenharmony_ci std::sort(v.begin(), v.end(), CompareCoverageBlock); 691cb0ef41Sopenharmony_ci} 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_cistd::vector<CoverageBlock> GetSortedBlockData(SharedFunctionInfo shared) { 721cb0ef41Sopenharmony_ci DCHECK(shared.HasCoverageInfo()); 731cb0ef41Sopenharmony_ci 741cb0ef41Sopenharmony_ci CoverageInfo coverage_info = 751cb0ef41Sopenharmony_ci CoverageInfo::cast(shared.GetDebugInfo().coverage_info()); 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci std::vector<CoverageBlock> result; 781cb0ef41Sopenharmony_ci if (coverage_info.slot_count() == 0) return result; 791cb0ef41Sopenharmony_ci 801cb0ef41Sopenharmony_ci for (int i = 0; i < coverage_info.slot_count(); i++) { 811cb0ef41Sopenharmony_ci const int start_pos = coverage_info.slots_start_source_position(i); 821cb0ef41Sopenharmony_ci const int until_pos = coverage_info.slots_end_source_position(i); 831cb0ef41Sopenharmony_ci const int count = coverage_info.slots_block_count(i); 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci DCHECK_NE(kNoSourcePosition, start_pos); 861cb0ef41Sopenharmony_ci result.emplace_back(start_pos, until_pos, count); 871cb0ef41Sopenharmony_ci } 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ci SortBlockData(result); 901cb0ef41Sopenharmony_ci 911cb0ef41Sopenharmony_ci return result; 921cb0ef41Sopenharmony_ci} 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci// A utility class to simplify logic for performing passes over block coverage 951cb0ef41Sopenharmony_ci// ranges. Provides access to the implicit tree structure of ranges (i.e. access 961cb0ef41Sopenharmony_ci// to parent and sibling blocks), and supports efficient in-place editing and 971cb0ef41Sopenharmony_ci// deletion. The underlying backing store is the array of CoverageBlocks stored 981cb0ef41Sopenharmony_ci// on the CoverageFunction. 991cb0ef41Sopenharmony_ciclass CoverageBlockIterator final { 1001cb0ef41Sopenharmony_ci public: 1011cb0ef41Sopenharmony_ci explicit CoverageBlockIterator(CoverageFunction* function) 1021cb0ef41Sopenharmony_ci : function_(function) { 1031cb0ef41Sopenharmony_ci DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(), 1041cb0ef41Sopenharmony_ci CompareCoverageBlock)); 1051cb0ef41Sopenharmony_ci } 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci ~CoverageBlockIterator() { 1081cb0ef41Sopenharmony_ci Finalize(); 1091cb0ef41Sopenharmony_ci DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(), 1101cb0ef41Sopenharmony_ci CompareCoverageBlock)); 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci 1131cb0ef41Sopenharmony_ci bool HasNext() const { 1141cb0ef41Sopenharmony_ci return read_index_ + 1 < static_cast<int>(function_->blocks.size()); 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci bool Next() { 1181cb0ef41Sopenharmony_ci if (!HasNext()) { 1191cb0ef41Sopenharmony_ci if (!ended_) MaybeWriteCurrent(); 1201cb0ef41Sopenharmony_ci ended_ = true; 1211cb0ef41Sopenharmony_ci return false; 1221cb0ef41Sopenharmony_ci } 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_ci // If a block has been deleted, subsequent iteration moves trailing blocks 1251cb0ef41Sopenharmony_ci // to their updated position within the array. 1261cb0ef41Sopenharmony_ci MaybeWriteCurrent(); 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ci if (read_index_ == -1) { 1291cb0ef41Sopenharmony_ci // Initialize the nesting stack with the function range. 1301cb0ef41Sopenharmony_ci nesting_stack_.emplace_back(function_->start, function_->end, 1311cb0ef41Sopenharmony_ci function_->count); 1321cb0ef41Sopenharmony_ci } else if (!delete_current_) { 1331cb0ef41Sopenharmony_ci nesting_stack_.emplace_back(GetBlock()); 1341cb0ef41Sopenharmony_ci } 1351cb0ef41Sopenharmony_ci 1361cb0ef41Sopenharmony_ci delete_current_ = false; 1371cb0ef41Sopenharmony_ci read_index_++; 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci CoverageBlock& block = GetBlock(); 1421cb0ef41Sopenharmony_ci while (nesting_stack_.size() > 1 && 1431cb0ef41Sopenharmony_ci nesting_stack_.back().end <= block.start) { 1441cb0ef41Sopenharmony_ci nesting_stack_.pop_back(); 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci DCHECK_IMPLIES(block.start >= function_->end, 1481cb0ef41Sopenharmony_ci block.end == kNoSourcePosition); 1491cb0ef41Sopenharmony_ci DCHECK_NE(block.start, kNoSourcePosition); 1501cb0ef41Sopenharmony_ci DCHECK_LE(block.end, GetParent().end); 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci return true; 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci CoverageBlock& GetBlock() { 1561cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1571cb0ef41Sopenharmony_ci return function_->blocks[read_index_]; 1581cb0ef41Sopenharmony_ci } 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci CoverageBlock& GetNextBlock() { 1611cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1621cb0ef41Sopenharmony_ci DCHECK(HasNext()); 1631cb0ef41Sopenharmony_ci return function_->blocks[read_index_ + 1]; 1641cb0ef41Sopenharmony_ci } 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci CoverageBlock& GetPreviousBlock() { 1671cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1681cb0ef41Sopenharmony_ci DCHECK_GT(read_index_, 0); 1691cb0ef41Sopenharmony_ci return function_->blocks[read_index_ - 1]; 1701cb0ef41Sopenharmony_ci } 1711cb0ef41Sopenharmony_ci 1721cb0ef41Sopenharmony_ci CoverageBlock& GetParent() { 1731cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1741cb0ef41Sopenharmony_ci return nesting_stack_.back(); 1751cb0ef41Sopenharmony_ci } 1761cb0ef41Sopenharmony_ci 1771cb0ef41Sopenharmony_ci bool HasSiblingOrChild() { 1781cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1791cb0ef41Sopenharmony_ci return HasNext() && GetNextBlock().start < GetParent().end; 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci CoverageBlock& GetSiblingOrChild() { 1831cb0ef41Sopenharmony_ci DCHECK(HasSiblingOrChild()); 1841cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1851cb0ef41Sopenharmony_ci return GetNextBlock(); 1861cb0ef41Sopenharmony_ci } 1871cb0ef41Sopenharmony_ci 1881cb0ef41Sopenharmony_ci // A range is considered to be at top level if its parent range is the 1891cb0ef41Sopenharmony_ci // function range. 1901cb0ef41Sopenharmony_ci bool IsTopLevel() const { return nesting_stack_.size() == 1; } 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci void DeleteBlock() { 1931cb0ef41Sopenharmony_ci DCHECK(!delete_current_); 1941cb0ef41Sopenharmony_ci DCHECK(IsActive()); 1951cb0ef41Sopenharmony_ci delete_current_ = true; 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci private: 1991cb0ef41Sopenharmony_ci void MaybeWriteCurrent() { 2001cb0ef41Sopenharmony_ci if (delete_current_) return; 2011cb0ef41Sopenharmony_ci if (read_index_ >= 0 && write_index_ != read_index_) { 2021cb0ef41Sopenharmony_ci function_->blocks[write_index_] = function_->blocks[read_index_]; 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci write_index_++; 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci void Finalize() { 2081cb0ef41Sopenharmony_ci while (Next()) { 2091cb0ef41Sopenharmony_ci // Just iterate to the end. 2101cb0ef41Sopenharmony_ci } 2111cb0ef41Sopenharmony_ci function_->blocks.resize(write_index_); 2121cb0ef41Sopenharmony_ci } 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci bool IsActive() const { return read_index_ >= 0 && !ended_; } 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci CoverageFunction* function_; 2171cb0ef41Sopenharmony_ci std::vector<CoverageBlock> nesting_stack_; 2181cb0ef41Sopenharmony_ci bool ended_ = false; 2191cb0ef41Sopenharmony_ci bool delete_current_ = false; 2201cb0ef41Sopenharmony_ci int read_index_ = -1; 2211cb0ef41Sopenharmony_ci int write_index_ = -1; 2221cb0ef41Sopenharmony_ci}; 2231cb0ef41Sopenharmony_ci 2241cb0ef41Sopenharmony_cibool HaveSameSourceRange(const CoverageBlock& lhs, const CoverageBlock& rhs) { 2251cb0ef41Sopenharmony_ci return lhs.start == rhs.start && lhs.end == rhs.end; 2261cb0ef41Sopenharmony_ci} 2271cb0ef41Sopenharmony_ci 2281cb0ef41Sopenharmony_civoid MergeDuplicateRanges(CoverageFunction* function) { 2291cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 2301cb0ef41Sopenharmony_ci 2311cb0ef41Sopenharmony_ci while (iter.Next() && iter.HasNext()) { 2321cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 2331cb0ef41Sopenharmony_ci CoverageBlock& next_block = iter.GetNextBlock(); 2341cb0ef41Sopenharmony_ci 2351cb0ef41Sopenharmony_ci if (!HaveSameSourceRange(block, next_block)) continue; 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci DCHECK_NE(kNoSourcePosition, block.end); // Non-singleton range. 2381cb0ef41Sopenharmony_ci next_block.count = std::max(block.count, next_block.count); 2391cb0ef41Sopenharmony_ci iter.DeleteBlock(); 2401cb0ef41Sopenharmony_ci } 2411cb0ef41Sopenharmony_ci} 2421cb0ef41Sopenharmony_ci 2431cb0ef41Sopenharmony_ci// Rewrite position singletons (produced by unconditional control flow 2441cb0ef41Sopenharmony_ci// like return statements, and by continuation counters) into source 2451cb0ef41Sopenharmony_ci// ranges that end at the next sibling range or the end of the parent 2461cb0ef41Sopenharmony_ci// range, whichever comes first. 2471cb0ef41Sopenharmony_civoid RewritePositionSingletonsToRanges(CoverageFunction* function) { 2481cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci while (iter.Next()) { 2511cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 2521cb0ef41Sopenharmony_ci CoverageBlock& parent = iter.GetParent(); 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci if (block.start >= function->end) { 2551cb0ef41Sopenharmony_ci DCHECK_EQ(block.end, kNoSourcePosition); 2561cb0ef41Sopenharmony_ci iter.DeleteBlock(); 2571cb0ef41Sopenharmony_ci } else if (block.end == kNoSourcePosition) { 2581cb0ef41Sopenharmony_ci // The current block ends at the next sibling block (if it exists) or the 2591cb0ef41Sopenharmony_ci // end of the parent block otherwise. 2601cb0ef41Sopenharmony_ci if (iter.HasSiblingOrChild()) { 2611cb0ef41Sopenharmony_ci block.end = iter.GetSiblingOrChild().start; 2621cb0ef41Sopenharmony_ci } else if (iter.IsTopLevel()) { 2631cb0ef41Sopenharmony_ci // See https://crbug.com/v8/6661. Functions are special-cased because 2641cb0ef41Sopenharmony_ci // we never want the closing brace to be uncovered. This is mainly to 2651cb0ef41Sopenharmony_ci // avoid a noisy UI. 2661cb0ef41Sopenharmony_ci block.end = parent.end - 1; 2671cb0ef41Sopenharmony_ci } else { 2681cb0ef41Sopenharmony_ci block.end = parent.end; 2691cb0ef41Sopenharmony_ci } 2701cb0ef41Sopenharmony_ci } 2711cb0ef41Sopenharmony_ci } 2721cb0ef41Sopenharmony_ci} 2731cb0ef41Sopenharmony_ci 2741cb0ef41Sopenharmony_civoid MergeConsecutiveRanges(CoverageFunction* function) { 2751cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 2761cb0ef41Sopenharmony_ci 2771cb0ef41Sopenharmony_ci while (iter.Next()) { 2781cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 2791cb0ef41Sopenharmony_ci 2801cb0ef41Sopenharmony_ci if (iter.HasSiblingOrChild()) { 2811cb0ef41Sopenharmony_ci CoverageBlock& sibling = iter.GetSiblingOrChild(); 2821cb0ef41Sopenharmony_ci if (sibling.start == block.end && sibling.count == block.count) { 2831cb0ef41Sopenharmony_ci // Best-effort: this pass may miss mergeable siblings in the presence of 2841cb0ef41Sopenharmony_ci // child blocks. 2851cb0ef41Sopenharmony_ci sibling.start = block.start; 2861cb0ef41Sopenharmony_ci iter.DeleteBlock(); 2871cb0ef41Sopenharmony_ci } 2881cb0ef41Sopenharmony_ci } 2891cb0ef41Sopenharmony_ci } 2901cb0ef41Sopenharmony_ci} 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_civoid MergeNestedRanges(CoverageFunction* function) { 2931cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ci while (iter.Next()) { 2961cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 2971cb0ef41Sopenharmony_ci CoverageBlock& parent = iter.GetParent(); 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci if (parent.count == block.count) { 3001cb0ef41Sopenharmony_ci // Transformation may not be valid if sibling blocks exist with a 3011cb0ef41Sopenharmony_ci // differing count. 3021cb0ef41Sopenharmony_ci iter.DeleteBlock(); 3031cb0ef41Sopenharmony_ci } 3041cb0ef41Sopenharmony_ci } 3051cb0ef41Sopenharmony_ci} 3061cb0ef41Sopenharmony_ci 3071cb0ef41Sopenharmony_civoid RewriteFunctionScopeCounter(CoverageFunction* function) { 3081cb0ef41Sopenharmony_ci // Every function must have at least the top-level function counter. 3091cb0ef41Sopenharmony_ci DCHECK(!function->blocks.empty()); 3101cb0ef41Sopenharmony_ci 3111cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 3121cb0ef41Sopenharmony_ci if (iter.Next()) { 3131cb0ef41Sopenharmony_ci DCHECK(iter.IsTopLevel()); 3141cb0ef41Sopenharmony_ci 3151cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 3161cb0ef41Sopenharmony_ci if (block.start == SourceRange::kFunctionLiteralSourcePosition && 3171cb0ef41Sopenharmony_ci block.end == SourceRange::kFunctionLiteralSourcePosition) { 3181cb0ef41Sopenharmony_ci // If a function-scope block exists, overwrite the function count. It has 3191cb0ef41Sopenharmony_ci // a more reliable count than what we get from the FeedbackVector (which 3201cb0ef41Sopenharmony_ci // is imprecise e.g. for generator functions and optimized code). 3211cb0ef41Sopenharmony_ci function->count = block.count; 3221cb0ef41Sopenharmony_ci 3231cb0ef41Sopenharmony_ci // Then delete it; for compatibility with non-block coverage modes, the 3241cb0ef41Sopenharmony_ci // function-scope block is expected in CoverageFunction, not as a 3251cb0ef41Sopenharmony_ci // CoverageBlock. 3261cb0ef41Sopenharmony_ci iter.DeleteBlock(); 3271cb0ef41Sopenharmony_ci } 3281cb0ef41Sopenharmony_ci } 3291cb0ef41Sopenharmony_ci} 3301cb0ef41Sopenharmony_ci 3311cb0ef41Sopenharmony_civoid FilterAliasedSingletons(CoverageFunction* function) { 3321cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 3331cb0ef41Sopenharmony_ci 3341cb0ef41Sopenharmony_ci iter.Next(); // Advance once since we reference the previous block later. 3351cb0ef41Sopenharmony_ci 3361cb0ef41Sopenharmony_ci while (iter.Next()) { 3371cb0ef41Sopenharmony_ci CoverageBlock& previous_block = iter.GetPreviousBlock(); 3381cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 3391cb0ef41Sopenharmony_ci 3401cb0ef41Sopenharmony_ci bool is_singleton = block.end == kNoSourcePosition; 3411cb0ef41Sopenharmony_ci bool aliases_start = block.start == previous_block.start; 3421cb0ef41Sopenharmony_ci 3431cb0ef41Sopenharmony_ci if (is_singleton && aliases_start) { 3441cb0ef41Sopenharmony_ci // The previous block must have a full range since duplicate singletons 3451cb0ef41Sopenharmony_ci // have already been merged. 3461cb0ef41Sopenharmony_ci DCHECK_NE(previous_block.end, kNoSourcePosition); 3471cb0ef41Sopenharmony_ci // Likewise, the next block must have another start position since 3481cb0ef41Sopenharmony_ci // singletons are sorted to the end. 3491cb0ef41Sopenharmony_ci DCHECK_IMPLIES(iter.HasNext(), iter.GetNextBlock().start != block.start); 3501cb0ef41Sopenharmony_ci iter.DeleteBlock(); 3511cb0ef41Sopenharmony_ci } 3521cb0ef41Sopenharmony_ci } 3531cb0ef41Sopenharmony_ci} 3541cb0ef41Sopenharmony_ci 3551cb0ef41Sopenharmony_civoid FilterUncoveredRanges(CoverageFunction* function) { 3561cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 3571cb0ef41Sopenharmony_ci 3581cb0ef41Sopenharmony_ci while (iter.Next()) { 3591cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 3601cb0ef41Sopenharmony_ci CoverageBlock& parent = iter.GetParent(); 3611cb0ef41Sopenharmony_ci if (block.count == 0 && parent.count == 0) iter.DeleteBlock(); 3621cb0ef41Sopenharmony_ci } 3631cb0ef41Sopenharmony_ci} 3641cb0ef41Sopenharmony_ci 3651cb0ef41Sopenharmony_civoid FilterEmptyRanges(CoverageFunction* function) { 3661cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_ci while (iter.Next()) { 3691cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 3701cb0ef41Sopenharmony_ci if (block.start == block.end) iter.DeleteBlock(); 3711cb0ef41Sopenharmony_ci } 3721cb0ef41Sopenharmony_ci} 3731cb0ef41Sopenharmony_ci 3741cb0ef41Sopenharmony_civoid ClampToBinary(CoverageFunction* function) { 3751cb0ef41Sopenharmony_ci CoverageBlockIterator iter(function); 3761cb0ef41Sopenharmony_ci 3771cb0ef41Sopenharmony_ci while (iter.Next()) { 3781cb0ef41Sopenharmony_ci CoverageBlock& block = iter.GetBlock(); 3791cb0ef41Sopenharmony_ci if (block.count > 0) block.count = 1; 3801cb0ef41Sopenharmony_ci } 3811cb0ef41Sopenharmony_ci} 3821cb0ef41Sopenharmony_ci 3831cb0ef41Sopenharmony_civoid ResetAllBlockCounts(SharedFunctionInfo shared) { 3841cb0ef41Sopenharmony_ci DCHECK(shared.HasCoverageInfo()); 3851cb0ef41Sopenharmony_ci 3861cb0ef41Sopenharmony_ci CoverageInfo coverage_info = 3871cb0ef41Sopenharmony_ci CoverageInfo::cast(shared.GetDebugInfo().coverage_info()); 3881cb0ef41Sopenharmony_ci 3891cb0ef41Sopenharmony_ci for (int i = 0; i < coverage_info.slot_count(); i++) { 3901cb0ef41Sopenharmony_ci coverage_info.ResetBlockCount(i); 3911cb0ef41Sopenharmony_ci } 3921cb0ef41Sopenharmony_ci} 3931cb0ef41Sopenharmony_ci 3941cb0ef41Sopenharmony_cibool IsBlockMode(debug::CoverageMode mode) { 3951cb0ef41Sopenharmony_ci switch (mode) { 3961cb0ef41Sopenharmony_ci case debug::CoverageMode::kBlockBinary: 3971cb0ef41Sopenharmony_ci case debug::CoverageMode::kBlockCount: 3981cb0ef41Sopenharmony_ci return true; 3991cb0ef41Sopenharmony_ci default: 4001cb0ef41Sopenharmony_ci return false; 4011cb0ef41Sopenharmony_ci } 4021cb0ef41Sopenharmony_ci} 4031cb0ef41Sopenharmony_ci 4041cb0ef41Sopenharmony_cibool IsBinaryMode(debug::CoverageMode mode) { 4051cb0ef41Sopenharmony_ci switch (mode) { 4061cb0ef41Sopenharmony_ci case debug::CoverageMode::kBlockBinary: 4071cb0ef41Sopenharmony_ci case debug::CoverageMode::kPreciseBinary: 4081cb0ef41Sopenharmony_ci return true; 4091cb0ef41Sopenharmony_ci default: 4101cb0ef41Sopenharmony_ci return false; 4111cb0ef41Sopenharmony_ci } 4121cb0ef41Sopenharmony_ci} 4131cb0ef41Sopenharmony_ci 4141cb0ef41Sopenharmony_civoid CollectBlockCoverageInternal(CoverageFunction* function, 4151cb0ef41Sopenharmony_ci SharedFunctionInfo info, 4161cb0ef41Sopenharmony_ci debug::CoverageMode mode) { 4171cb0ef41Sopenharmony_ci DCHECK(IsBlockMode(mode)); 4181cb0ef41Sopenharmony_ci 4191cb0ef41Sopenharmony_ci // Functions with empty source ranges are not interesting to report. This can 4201cb0ef41Sopenharmony_ci // happen e.g. for internally-generated functions like class constructors. 4211cb0ef41Sopenharmony_ci if (!function->HasNonEmptySourceRange()) return; 4221cb0ef41Sopenharmony_ci 4231cb0ef41Sopenharmony_ci function->has_block_coverage = true; 4241cb0ef41Sopenharmony_ci function->blocks = GetSortedBlockData(info); 4251cb0ef41Sopenharmony_ci 4261cb0ef41Sopenharmony_ci // If in binary mode, only report counts of 0/1. 4271cb0ef41Sopenharmony_ci if (mode == debug::CoverageMode::kBlockBinary) ClampToBinary(function); 4281cb0ef41Sopenharmony_ci 4291cb0ef41Sopenharmony_ci // To stay compatible with non-block coverage modes, the function-scope count 4301cb0ef41Sopenharmony_ci // is expected to be in the CoverageFunction, not as part of its blocks. 4311cb0ef41Sopenharmony_ci // This finds the function-scope counter, overwrites CoverageFunction::count, 4321cb0ef41Sopenharmony_ci // and removes it from the block list. 4331cb0ef41Sopenharmony_ci // 4341cb0ef41Sopenharmony_ci // Important: Must be called before other transformation passes. 4351cb0ef41Sopenharmony_ci RewriteFunctionScopeCounter(function); 4361cb0ef41Sopenharmony_ci 4371cb0ef41Sopenharmony_ci // Functions without blocks don't need to be processed further. 4381cb0ef41Sopenharmony_ci if (!function->HasBlocks()) return; 4391cb0ef41Sopenharmony_ci 4401cb0ef41Sopenharmony_ci // Remove singleton ranges with the same start position as a full range and 4411cb0ef41Sopenharmony_ci // throw away their counts. 4421cb0ef41Sopenharmony_ci // Singleton ranges are only intended to split existing full ranges and should 4431cb0ef41Sopenharmony_ci // never expand into a full range. Consider 'if (cond) { ... } else { ... }' 4441cb0ef41Sopenharmony_ci // as a problematic example; if the then-block produces a continuation 4451cb0ef41Sopenharmony_ci // singleton, it would incorrectly expand into the else range. 4461cb0ef41Sopenharmony_ci // For more context, see https://crbug.com/v8/8237. 4471cb0ef41Sopenharmony_ci FilterAliasedSingletons(function); 4481cb0ef41Sopenharmony_ci 4491cb0ef41Sopenharmony_ci // Rewrite all singletons (created e.g. by continuations and unconditional 4501cb0ef41Sopenharmony_ci // control flow) to ranges. 4511cb0ef41Sopenharmony_ci RewritePositionSingletonsToRanges(function); 4521cb0ef41Sopenharmony_ci 4531cb0ef41Sopenharmony_ci // Merge nested and consecutive ranges with identical counts. 4541cb0ef41Sopenharmony_ci // Note that it's necessary to merge duplicate ranges prior to merging nested 4551cb0ef41Sopenharmony_ci // changes in order to avoid invalid transformations. See crbug.com/827530. 4561cb0ef41Sopenharmony_ci MergeConsecutiveRanges(function); 4571cb0ef41Sopenharmony_ci 4581cb0ef41Sopenharmony_ci SortBlockData(function->blocks); 4591cb0ef41Sopenharmony_ci MergeDuplicateRanges(function); 4601cb0ef41Sopenharmony_ci MergeNestedRanges(function); 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_ci MergeConsecutiveRanges(function); 4631cb0ef41Sopenharmony_ci 4641cb0ef41Sopenharmony_ci // Filter out ranges with count == 0 unless the immediate parent range has 4651cb0ef41Sopenharmony_ci // a count != 0. 4661cb0ef41Sopenharmony_ci FilterUncoveredRanges(function); 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_ci // Filter out ranges of zero length. 4691cb0ef41Sopenharmony_ci FilterEmptyRanges(function); 4701cb0ef41Sopenharmony_ci} 4711cb0ef41Sopenharmony_ci 4721cb0ef41Sopenharmony_civoid CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info, 4731cb0ef41Sopenharmony_ci debug::CoverageMode mode) { 4741cb0ef41Sopenharmony_ci CollectBlockCoverageInternal(function, info, mode); 4751cb0ef41Sopenharmony_ci 4761cb0ef41Sopenharmony_ci // Reset all counters on the DebugInfo to zero. 4771cb0ef41Sopenharmony_ci ResetAllBlockCounts(info); 4781cb0ef41Sopenharmony_ci} 4791cb0ef41Sopenharmony_ci 4801cb0ef41Sopenharmony_civoid PrintBlockCoverage(const CoverageFunction* function, 4811cb0ef41Sopenharmony_ci SharedFunctionInfo info, bool has_nonempty_source_range, 4821cb0ef41Sopenharmony_ci bool function_is_relevant) { 4831cb0ef41Sopenharmony_ci DCHECK(FLAG_trace_block_coverage); 4841cb0ef41Sopenharmony_ci std::unique_ptr<char[]> function_name = 4851cb0ef41Sopenharmony_ci function->name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); 4861cb0ef41Sopenharmony_ci i::PrintF( 4871cb0ef41Sopenharmony_ci "Coverage for function='%s', SFI=%p, has_nonempty_source_range=%d, " 4881cb0ef41Sopenharmony_ci "function_is_relevant=%d\n", 4891cb0ef41Sopenharmony_ci function_name.get(), reinterpret_cast<void*>(info.ptr()), 4901cb0ef41Sopenharmony_ci has_nonempty_source_range, function_is_relevant); 4911cb0ef41Sopenharmony_ci i::PrintF("{start: %d, end: %d, count: %d}\n", function->start, function->end, 4921cb0ef41Sopenharmony_ci function->count); 4931cb0ef41Sopenharmony_ci for (const auto& block : function->blocks) { 4941cb0ef41Sopenharmony_ci i::PrintF("{start: %d, end: %d, count: %d}\n", block.start, block.end, 4951cb0ef41Sopenharmony_ci block.count); 4961cb0ef41Sopenharmony_ci } 4971cb0ef41Sopenharmony_ci} 4981cb0ef41Sopenharmony_ci 4991cb0ef41Sopenharmony_civoid CollectAndMaybeResetCounts(Isolate* isolate, 5001cb0ef41Sopenharmony_ci SharedToCounterMap* counter_map, 5011cb0ef41Sopenharmony_ci v8::debug::CoverageMode coverage_mode) { 5021cb0ef41Sopenharmony_ci const bool reset_count = 5031cb0ef41Sopenharmony_ci coverage_mode != v8::debug::CoverageMode::kBestEffort; 5041cb0ef41Sopenharmony_ci 5051cb0ef41Sopenharmony_ci switch (isolate->code_coverage_mode()) { 5061cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBlockBinary: 5071cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBlockCount: 5081cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kPreciseBinary: 5091cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kPreciseCount: { 5101cb0ef41Sopenharmony_ci // Feedback vectors are already listed to prevent losing them to GC. 5111cb0ef41Sopenharmony_ci DCHECK(isolate->factory() 5121cb0ef41Sopenharmony_ci ->feedback_vectors_for_profiling_tools() 5131cb0ef41Sopenharmony_ci ->IsArrayList()); 5141cb0ef41Sopenharmony_ci Handle<ArrayList> list = Handle<ArrayList>::cast( 5151cb0ef41Sopenharmony_ci isolate->factory()->feedback_vectors_for_profiling_tools()); 5161cb0ef41Sopenharmony_ci for (int i = 0; i < list->Length(); i++) { 5171cb0ef41Sopenharmony_ci FeedbackVector vector = FeedbackVector::cast(list->Get(i)); 5181cb0ef41Sopenharmony_ci SharedFunctionInfo shared = vector.shared_function_info(); 5191cb0ef41Sopenharmony_ci DCHECK(shared.IsSubjectToDebugging()); 5201cb0ef41Sopenharmony_ci uint32_t count = static_cast<uint32_t>(vector.invocation_count()); 5211cb0ef41Sopenharmony_ci if (reset_count) vector.clear_invocation_count(kRelaxedStore); 5221cb0ef41Sopenharmony_ci counter_map->Add(shared, count); 5231cb0ef41Sopenharmony_ci } 5241cb0ef41Sopenharmony_ci break; 5251cb0ef41Sopenharmony_ci } 5261cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBestEffort: { 5271cb0ef41Sopenharmony_ci DCHECK(!isolate->factory() 5281cb0ef41Sopenharmony_ci ->feedback_vectors_for_profiling_tools() 5291cb0ef41Sopenharmony_ci ->IsArrayList()); 5301cb0ef41Sopenharmony_ci DCHECK_EQ(v8::debug::CoverageMode::kBestEffort, coverage_mode); 5311cb0ef41Sopenharmony_ci AllowGarbageCollection allow_gc; 5321cb0ef41Sopenharmony_ci HeapObjectIterator heap_iterator(isolate->heap()); 5331cb0ef41Sopenharmony_ci for (HeapObject current_obj = heap_iterator.Next(); 5341cb0ef41Sopenharmony_ci !current_obj.is_null(); current_obj = heap_iterator.Next()) { 5351cb0ef41Sopenharmony_ci if (!current_obj.IsJSFunction()) continue; 5361cb0ef41Sopenharmony_ci JSFunction func = JSFunction::cast(current_obj); 5371cb0ef41Sopenharmony_ci SharedFunctionInfo shared = func.shared(); 5381cb0ef41Sopenharmony_ci if (!shared.IsSubjectToDebugging()) continue; 5391cb0ef41Sopenharmony_ci if (!(func.has_feedback_vector() || 5401cb0ef41Sopenharmony_ci func.has_closure_feedback_cell_array())) { 5411cb0ef41Sopenharmony_ci continue; 5421cb0ef41Sopenharmony_ci } 5431cb0ef41Sopenharmony_ci uint32_t count = 0; 5441cb0ef41Sopenharmony_ci if (func.has_feedback_vector()) { 5451cb0ef41Sopenharmony_ci count = 5461cb0ef41Sopenharmony_ci static_cast<uint32_t>(func.feedback_vector().invocation_count()); 5471cb0ef41Sopenharmony_ci } else if (func.raw_feedback_cell().interrupt_budget() < 5481cb0ef41Sopenharmony_ci FLAG_interrupt_budget_for_feedback_allocation) { 5491cb0ef41Sopenharmony_ci // TODO(jgruber): The condition above is no longer precise since we 5501cb0ef41Sopenharmony_ci // may use either the fixed interrupt_budget or 5511cb0ef41Sopenharmony_ci // FLAG_interrupt_budget_factor_for_feedback_allocation. If the 5521cb0ef41Sopenharmony_ci // latter, we may incorrectly set a count of 1. 5531cb0ef41Sopenharmony_ci // 5541cb0ef41Sopenharmony_ci // We haven't allocated feedback vector, but executed the function 5551cb0ef41Sopenharmony_ci // atleast once. We don't have precise invocation count here. 5561cb0ef41Sopenharmony_ci count = 1; 5571cb0ef41Sopenharmony_ci } 5581cb0ef41Sopenharmony_ci counter_map->Add(shared, count); 5591cb0ef41Sopenharmony_ci } 5601cb0ef41Sopenharmony_ci 5611cb0ef41Sopenharmony_ci // Also check functions on the stack to collect the count map. With lazy 5621cb0ef41Sopenharmony_ci // feedback allocation we may miss counting functions if the feedback 5631cb0ef41Sopenharmony_ci // vector wasn't allocated yet and the function's interrupt budget wasn't 5641cb0ef41Sopenharmony_ci // updated (i.e. it didn't execute return / jump). 5651cb0ef41Sopenharmony_ci for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { 5661cb0ef41Sopenharmony_ci SharedFunctionInfo shared = it.frame()->function().shared(); 5671cb0ef41Sopenharmony_ci if (counter_map->Get(shared) != 0) continue; 5681cb0ef41Sopenharmony_ci counter_map->Add(shared, 1); 5691cb0ef41Sopenharmony_ci } 5701cb0ef41Sopenharmony_ci break; 5711cb0ef41Sopenharmony_ci } 5721cb0ef41Sopenharmony_ci } 5731cb0ef41Sopenharmony_ci} 5741cb0ef41Sopenharmony_ci 5751cb0ef41Sopenharmony_ci// A {SFI, count} tuple is used to sort by source range (stored on 5761cb0ef41Sopenharmony_ci// the SFI) and call count (in the counter map). 5771cb0ef41Sopenharmony_cistruct SharedFunctionInfoAndCount { 5781cb0ef41Sopenharmony_ci SharedFunctionInfoAndCount(Handle<SharedFunctionInfo> info, uint32_t count) 5791cb0ef41Sopenharmony_ci : info(info), 5801cb0ef41Sopenharmony_ci count(count), 5811cb0ef41Sopenharmony_ci start(StartPosition(*info)), 5821cb0ef41Sopenharmony_ci end(info->EndPosition()) {} 5831cb0ef41Sopenharmony_ci 5841cb0ef41Sopenharmony_ci // Sort by: 5851cb0ef41Sopenharmony_ci // - start, ascending. 5861cb0ef41Sopenharmony_ci // - end, descending. 5871cb0ef41Sopenharmony_ci // - info.is_toplevel() first 5881cb0ef41Sopenharmony_ci // - count, descending. 5891cb0ef41Sopenharmony_ci bool operator<(const SharedFunctionInfoAndCount& that) const { 5901cb0ef41Sopenharmony_ci if (this->start != that.start) return this->start < that.start; 5911cb0ef41Sopenharmony_ci if (this->end != that.end) return this->end > that.end; 5921cb0ef41Sopenharmony_ci if (this->info->is_toplevel() != that.info->is_toplevel()) { 5931cb0ef41Sopenharmony_ci return this->info->is_toplevel(); 5941cb0ef41Sopenharmony_ci } 5951cb0ef41Sopenharmony_ci return this->count > that.count; 5961cb0ef41Sopenharmony_ci } 5971cb0ef41Sopenharmony_ci 5981cb0ef41Sopenharmony_ci Handle<SharedFunctionInfo> info; 5991cb0ef41Sopenharmony_ci uint32_t count; 6001cb0ef41Sopenharmony_ci int start; 6011cb0ef41Sopenharmony_ci int end; 6021cb0ef41Sopenharmony_ci}; 6031cb0ef41Sopenharmony_ci 6041cb0ef41Sopenharmony_ci} // anonymous namespace 6051cb0ef41Sopenharmony_ci 6061cb0ef41Sopenharmony_cistd::unique_ptr<Coverage> Coverage::CollectPrecise(Isolate* isolate) { 6071cb0ef41Sopenharmony_ci DCHECK(!isolate->is_best_effort_code_coverage()); 6081cb0ef41Sopenharmony_ci std::unique_ptr<Coverage> result = 6091cb0ef41Sopenharmony_ci Collect(isolate, isolate->code_coverage_mode()); 6101cb0ef41Sopenharmony_ci if (!isolate->is_collecting_type_profile() && 6111cb0ef41Sopenharmony_ci (isolate->is_precise_binary_code_coverage() || 6121cb0ef41Sopenharmony_ci isolate->is_block_binary_code_coverage())) { 6131cb0ef41Sopenharmony_ci // We do not have to hold onto feedback vectors for invocations we already 6141cb0ef41Sopenharmony_ci // reported. So we can reset the list. 6151cb0ef41Sopenharmony_ci isolate->SetFeedbackVectorsForProfilingTools( 6161cb0ef41Sopenharmony_ci ReadOnlyRoots(isolate).empty_array_list()); 6171cb0ef41Sopenharmony_ci } 6181cb0ef41Sopenharmony_ci return result; 6191cb0ef41Sopenharmony_ci} 6201cb0ef41Sopenharmony_ci 6211cb0ef41Sopenharmony_cistd::unique_ptr<Coverage> Coverage::CollectBestEffort(Isolate* isolate) { 6221cb0ef41Sopenharmony_ci return Collect(isolate, v8::debug::CoverageMode::kBestEffort); 6231cb0ef41Sopenharmony_ci} 6241cb0ef41Sopenharmony_ci 6251cb0ef41Sopenharmony_cistd::unique_ptr<Coverage> Coverage::Collect( 6261cb0ef41Sopenharmony_ci Isolate* isolate, v8::debug::CoverageMode collectionMode) { 6271cb0ef41Sopenharmony_ci // Collect call counts for all functions. 6281cb0ef41Sopenharmony_ci SharedToCounterMap counter_map; 6291cb0ef41Sopenharmony_ci CollectAndMaybeResetCounts(isolate, &counter_map, collectionMode); 6301cb0ef41Sopenharmony_ci 6311cb0ef41Sopenharmony_ci // Iterate shared function infos of every script and build a mapping 6321cb0ef41Sopenharmony_ci // between source ranges and invocation counts. 6331cb0ef41Sopenharmony_ci std::unique_ptr<Coverage> result(new Coverage()); 6341cb0ef41Sopenharmony_ci 6351cb0ef41Sopenharmony_ci std::vector<Handle<Script>> scripts; 6361cb0ef41Sopenharmony_ci Script::Iterator scriptIt(isolate); 6371cb0ef41Sopenharmony_ci for (Script script = scriptIt.Next(); !script.is_null(); 6381cb0ef41Sopenharmony_ci script = scriptIt.Next()) { 6391cb0ef41Sopenharmony_ci if (script.IsUserJavaScript()) scripts.push_back(handle(script, isolate)); 6401cb0ef41Sopenharmony_ci } 6411cb0ef41Sopenharmony_ci 6421cb0ef41Sopenharmony_ci for (Handle<Script> script : scripts) { 6431cb0ef41Sopenharmony_ci // Create and add new script data. 6441cb0ef41Sopenharmony_ci result->emplace_back(script); 6451cb0ef41Sopenharmony_ci std::vector<CoverageFunction>* functions = &result->back().functions; 6461cb0ef41Sopenharmony_ci 6471cb0ef41Sopenharmony_ci std::vector<SharedFunctionInfoAndCount> sorted; 6481cb0ef41Sopenharmony_ci 6491cb0ef41Sopenharmony_ci { 6501cb0ef41Sopenharmony_ci // Sort functions by start position, from outer to inner functions. 6511cb0ef41Sopenharmony_ci SharedFunctionInfo::ScriptIterator infos(isolate, *script); 6521cb0ef41Sopenharmony_ci for (SharedFunctionInfo info = infos.Next(); !info.is_null(); 6531cb0ef41Sopenharmony_ci info = infos.Next()) { 6541cb0ef41Sopenharmony_ci sorted.emplace_back(handle(info, isolate), counter_map.Get(info)); 6551cb0ef41Sopenharmony_ci } 6561cb0ef41Sopenharmony_ci std::sort(sorted.begin(), sorted.end()); 6571cb0ef41Sopenharmony_ci } 6581cb0ef41Sopenharmony_ci 6591cb0ef41Sopenharmony_ci // Stack to track nested functions, referring function by index. 6601cb0ef41Sopenharmony_ci std::vector<size_t> nesting; 6611cb0ef41Sopenharmony_ci 6621cb0ef41Sopenharmony_ci // Use sorted list to reconstruct function nesting. 6631cb0ef41Sopenharmony_ci for (const SharedFunctionInfoAndCount& v : sorted) { 6641cb0ef41Sopenharmony_ci Handle<SharedFunctionInfo> info = v.info; 6651cb0ef41Sopenharmony_ci int start = v.start; 6661cb0ef41Sopenharmony_ci int end = v.end; 6671cb0ef41Sopenharmony_ci uint32_t count = v.count; 6681cb0ef41Sopenharmony_ci 6691cb0ef41Sopenharmony_ci // Find the correct outer function based on start position. 6701cb0ef41Sopenharmony_ci // 6711cb0ef41Sopenharmony_ci // This is, in general, not robust when considering two functions with 6721cb0ef41Sopenharmony_ci // identical source ranges; then the notion of inner and outer is unclear. 6731cb0ef41Sopenharmony_ci // Identical source ranges arise when the source range of top-most entity 6741cb0ef41Sopenharmony_ci // (e.g. function) in the script is identical to the whole script, e.g. 6751cb0ef41Sopenharmony_ci // <script>function foo() {}<script>. The script has its own shared 6761cb0ef41Sopenharmony_ci // function info, which has the same source range as the SFI for `foo`. 6771cb0ef41Sopenharmony_ci // Node.js creates an additional wrapper for scripts (again with identical 6781cb0ef41Sopenharmony_ci // source range) and those wrappers will have a call count of zero even if 6791cb0ef41Sopenharmony_ci // the wrapped script was executed (see v8:9212). We mitigate this issue 6801cb0ef41Sopenharmony_ci // by sorting top-level SFIs first among SFIs with the same source range: 6811cb0ef41Sopenharmony_ci // This ensures top-level SFIs are processed first. If a top-level SFI has 6821cb0ef41Sopenharmony_ci // a non-zero call count, it gets recorded due to `function_is_relevant` 6831cb0ef41Sopenharmony_ci // below (e.g. script wrappers), while top-level SFIs with zero call count 6841cb0ef41Sopenharmony_ci // do not get reported (this ensures node's extra wrappers do not get 6851cb0ef41Sopenharmony_ci // reported). If two SFIs with identical source ranges get reported, we 6861cb0ef41Sopenharmony_ci // report them in decreasing order of call count, as in all known cases 6871cb0ef41Sopenharmony_ci // this corresponds to the nesting order. In the case of the script tag 6881cb0ef41Sopenharmony_ci // example above, we report the zero call count of `foo` last. As it turns 6891cb0ef41Sopenharmony_ci // out, embedders started to rely on functions being reported in nesting 6901cb0ef41Sopenharmony_ci // order. 6911cb0ef41Sopenharmony_ci // TODO(jgruber): Investigate whether it is possible to remove node's 6921cb0ef41Sopenharmony_ci // extra top-level wrapper script, or change its source range, or ensure 6931cb0ef41Sopenharmony_ci // that it follows the invariant that nesting order is descending count 6941cb0ef41Sopenharmony_ci // order for SFIs with identical source ranges. 6951cb0ef41Sopenharmony_ci while (!nesting.empty() && functions->at(nesting.back()).end <= start) { 6961cb0ef41Sopenharmony_ci nesting.pop_back(); 6971cb0ef41Sopenharmony_ci } 6981cb0ef41Sopenharmony_ci 6991cb0ef41Sopenharmony_ci if (count != 0) { 7001cb0ef41Sopenharmony_ci switch (collectionMode) { 7011cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBlockCount: 7021cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kPreciseCount: 7031cb0ef41Sopenharmony_ci break; 7041cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBlockBinary: 7051cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kPreciseBinary: 7061cb0ef41Sopenharmony_ci count = info->has_reported_binary_coverage() ? 0 : 1; 7071cb0ef41Sopenharmony_ci info->set_has_reported_binary_coverage(true); 7081cb0ef41Sopenharmony_ci break; 7091cb0ef41Sopenharmony_ci case v8::debug::CoverageMode::kBestEffort: 7101cb0ef41Sopenharmony_ci count = 1; 7111cb0ef41Sopenharmony_ci break; 7121cb0ef41Sopenharmony_ci } 7131cb0ef41Sopenharmony_ci } 7141cb0ef41Sopenharmony_ci 7151cb0ef41Sopenharmony_ci Handle<String> name = SharedFunctionInfo::DebugName(info); 7161cb0ef41Sopenharmony_ci CoverageFunction function(start, end, count, name); 7171cb0ef41Sopenharmony_ci 7181cb0ef41Sopenharmony_ci if (IsBlockMode(collectionMode) && info->HasCoverageInfo()) { 7191cb0ef41Sopenharmony_ci CollectBlockCoverage(&function, *info, collectionMode); 7201cb0ef41Sopenharmony_ci } 7211cb0ef41Sopenharmony_ci 7221cb0ef41Sopenharmony_ci // Only include a function range if itself or its parent function is 7231cb0ef41Sopenharmony_ci // covered, or if it contains non-trivial block coverage. 7241cb0ef41Sopenharmony_ci bool is_covered = (count != 0); 7251cb0ef41Sopenharmony_ci bool parent_is_covered = 7261cb0ef41Sopenharmony_ci (!nesting.empty() && functions->at(nesting.back()).count != 0); 7271cb0ef41Sopenharmony_ci bool has_block_coverage = !function.blocks.empty(); 7281cb0ef41Sopenharmony_ci bool function_is_relevant = 7291cb0ef41Sopenharmony_ci (is_covered || parent_is_covered || has_block_coverage); 7301cb0ef41Sopenharmony_ci 7311cb0ef41Sopenharmony_ci // It must also have a non-empty source range (otherwise it is not 7321cb0ef41Sopenharmony_ci // interesting to report). 7331cb0ef41Sopenharmony_ci bool has_nonempty_source_range = function.HasNonEmptySourceRange(); 7341cb0ef41Sopenharmony_ci 7351cb0ef41Sopenharmony_ci if (has_nonempty_source_range && function_is_relevant) { 7361cb0ef41Sopenharmony_ci nesting.push_back(functions->size()); 7371cb0ef41Sopenharmony_ci functions->emplace_back(function); 7381cb0ef41Sopenharmony_ci } 7391cb0ef41Sopenharmony_ci 7401cb0ef41Sopenharmony_ci if (FLAG_trace_block_coverage) { 7411cb0ef41Sopenharmony_ci PrintBlockCoverage(&function, *info, has_nonempty_source_range, 7421cb0ef41Sopenharmony_ci function_is_relevant); 7431cb0ef41Sopenharmony_ci } 7441cb0ef41Sopenharmony_ci } 7451cb0ef41Sopenharmony_ci 7461cb0ef41Sopenharmony_ci // Remove entries for scripts that have no coverage. 7471cb0ef41Sopenharmony_ci if (functions->empty()) result->pop_back(); 7481cb0ef41Sopenharmony_ci } 7491cb0ef41Sopenharmony_ci return result; 7501cb0ef41Sopenharmony_ci} 7511cb0ef41Sopenharmony_ci 7521cb0ef41Sopenharmony_civoid Coverage::SelectMode(Isolate* isolate, debug::CoverageMode mode) { 7531cb0ef41Sopenharmony_ci if (mode != isolate->code_coverage_mode()) { 7541cb0ef41Sopenharmony_ci // Changing the coverage mode can change the bytecode that would be 7551cb0ef41Sopenharmony_ci // generated for a function, which can interfere with lazy source positions, 7561cb0ef41Sopenharmony_ci // so just force source position collection whenever there's such a change. 7571cb0ef41Sopenharmony_ci isolate->CollectSourcePositionsForAllBytecodeArrays(); 7581cb0ef41Sopenharmony_ci // Changing the coverage mode changes the generated bytecode and hence it is 7591cb0ef41Sopenharmony_ci // not safe to flush bytecode. Set a flag here, so we can disable bytecode 7601cb0ef41Sopenharmony_ci // flushing. 7611cb0ef41Sopenharmony_ci isolate->set_disable_bytecode_flushing(true); 7621cb0ef41Sopenharmony_ci } 7631cb0ef41Sopenharmony_ci 7641cb0ef41Sopenharmony_ci switch (mode) { 7651cb0ef41Sopenharmony_ci case debug::CoverageMode::kBestEffort: 7661cb0ef41Sopenharmony_ci // Note that DevTools switches back to best-effort coverage once the 7671cb0ef41Sopenharmony_ci // recording is stopped. Since we delete coverage infos at that point, any 7681cb0ef41Sopenharmony_ci // following coverage recording (without reloads) will be at function 7691cb0ef41Sopenharmony_ci // granularity. 7701cb0ef41Sopenharmony_ci isolate->debug()->RemoveAllCoverageInfos(); 7711cb0ef41Sopenharmony_ci if (!isolate->is_collecting_type_profile()) { 7721cb0ef41Sopenharmony_ci isolate->SetFeedbackVectorsForProfilingTools( 7731cb0ef41Sopenharmony_ci ReadOnlyRoots(isolate).undefined_value()); 7741cb0ef41Sopenharmony_ci } 7751cb0ef41Sopenharmony_ci break; 7761cb0ef41Sopenharmony_ci case debug::CoverageMode::kBlockBinary: 7771cb0ef41Sopenharmony_ci case debug::CoverageMode::kBlockCount: 7781cb0ef41Sopenharmony_ci case debug::CoverageMode::kPreciseBinary: 7791cb0ef41Sopenharmony_ci case debug::CoverageMode::kPreciseCount: { 7801cb0ef41Sopenharmony_ci HandleScope scope(isolate); 7811cb0ef41Sopenharmony_ci 7821cb0ef41Sopenharmony_ci // Remove all optimized function. Optimized and inlined functions do not 7831cb0ef41Sopenharmony_ci // increment invocation count. 7841cb0ef41Sopenharmony_ci Deoptimizer::DeoptimizeAll(isolate); 7851cb0ef41Sopenharmony_ci 7861cb0ef41Sopenharmony_ci std::vector<Handle<JSFunction>> funcs_needing_feedback_vector; 7871cb0ef41Sopenharmony_ci { 7881cb0ef41Sopenharmony_ci HeapObjectIterator heap_iterator(isolate->heap()); 7891cb0ef41Sopenharmony_ci for (HeapObject o = heap_iterator.Next(); !o.is_null(); 7901cb0ef41Sopenharmony_ci o = heap_iterator.Next()) { 7911cb0ef41Sopenharmony_ci if (o.IsJSFunction()) { 7921cb0ef41Sopenharmony_ci JSFunction func = JSFunction::cast(o); 7931cb0ef41Sopenharmony_ci if (func.has_closure_feedback_cell_array()) { 7941cb0ef41Sopenharmony_ci funcs_needing_feedback_vector.push_back( 7951cb0ef41Sopenharmony_ci Handle<JSFunction>(func, isolate)); 7961cb0ef41Sopenharmony_ci } 7971cb0ef41Sopenharmony_ci } else if (IsBinaryMode(mode) && o.IsSharedFunctionInfo()) { 7981cb0ef41Sopenharmony_ci // If collecting binary coverage, reset 7991cb0ef41Sopenharmony_ci // SFI::has_reported_binary_coverage to avoid optimizing / inlining 8001cb0ef41Sopenharmony_ci // functions before they have reported coverage. 8011cb0ef41Sopenharmony_ci SharedFunctionInfo shared = SharedFunctionInfo::cast(o); 8021cb0ef41Sopenharmony_ci shared.set_has_reported_binary_coverage(false); 8031cb0ef41Sopenharmony_ci } else if (o.IsFeedbackVector()) { 8041cb0ef41Sopenharmony_ci // In any case, clear any collected invocation counts. 8051cb0ef41Sopenharmony_ci FeedbackVector::cast(o).clear_invocation_count(kRelaxedStore); 8061cb0ef41Sopenharmony_ci } 8071cb0ef41Sopenharmony_ci } 8081cb0ef41Sopenharmony_ci } 8091cb0ef41Sopenharmony_ci 8101cb0ef41Sopenharmony_ci for (Handle<JSFunction> func : funcs_needing_feedback_vector) { 8111cb0ef41Sopenharmony_ci IsCompiledScope is_compiled_scope( 8121cb0ef41Sopenharmony_ci func->shared().is_compiled_scope(isolate)); 8131cb0ef41Sopenharmony_ci CHECK(is_compiled_scope.is_compiled()); 8141cb0ef41Sopenharmony_ci JSFunction::EnsureFeedbackVector(isolate, func, &is_compiled_scope); 8151cb0ef41Sopenharmony_ci } 8161cb0ef41Sopenharmony_ci 8171cb0ef41Sopenharmony_ci // Root all feedback vectors to avoid early collection. 8181cb0ef41Sopenharmony_ci isolate->MaybeInitializeVectorListFromHeap(); 8191cb0ef41Sopenharmony_ci 8201cb0ef41Sopenharmony_ci break; 8211cb0ef41Sopenharmony_ci } 8221cb0ef41Sopenharmony_ci } 8231cb0ef41Sopenharmony_ci isolate->set_code_coverage_mode(mode); 8241cb0ef41Sopenharmony_ci} 8251cb0ef41Sopenharmony_ci 8261cb0ef41Sopenharmony_ci} // namespace internal 8271cb0ef41Sopenharmony_ci} // namespace v8 828