Commit c39123dd authored by yangguo's avatar yangguo Committed by Commit bot

[debugger] implement inspector-facing API for code coverage.

The inspector uses V8's API handles and should not access
V8 internals. This change makes sure it can use the coverage
data in an encapsulated way.

R=jgruber@chromium.org, kozyatinskiy@chromium.org
BUG=v8:5808

Review-Url: https://codereview.chromium.org/2696163002
Cr-Commit-Position: refs/heads/master@{#43231}
parent 277b8e93
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "src/contexts.h" #include "src/contexts.h"
#include "src/conversions-inl.h" #include "src/conversions-inl.h"
#include "src/counters.h" #include "src/counters.h"
#include "src/debug/debug-coverage.h"
#include "src/debug/debug.h" #include "src/debug/debug.h"
#include "src/deoptimizer.h" #include "src/deoptimizer.h"
#include "src/execution.h" #include "src/execution.h"
...@@ -9501,6 +9502,52 @@ Local<String> CpuProfileNode::GetFunctionName() const { ...@@ -9501,6 +9502,52 @@ Local<String> CpuProfileNode::GetFunctionName() const {
} }
} }
debug::Coverage::Range::Range(i::CoverageRange* range,
Local<debug::Script> script)
: range_(range), script_(script) {
i::Handle<i::Script> i_script = v8::Utils::OpenHandle(*script);
i::Script::PositionInfo start;
i::Script::PositionInfo end;
i::Script::GetPositionInfo(i_script, range->start, &start,
i::Script::WITH_OFFSET);
i::Script::GetPositionInfo(i_script, range->end, &end,
i::Script::WITH_OFFSET);
start_ = Location(start.line, start.column);
end_ = Location(end.line, end.column);
}
uint32_t debug::Coverage::Range::Count() { return range_->count; }
size_t debug::Coverage::Range::NestedCount() { return range_->inner.size(); }
debug::Coverage::Range debug::Coverage::Range::GetNested(size_t i) {
return Range(&range_->inner[i], script_);
}
MaybeLocal<String> debug::Coverage::Range::Name() {
return ToApiHandle<String>(range_->name);
}
debug::Coverage::~Coverage() { delete coverage_; }
size_t debug::Coverage::ScriptCount() { return coverage_->size(); }
Local<debug::Script> debug::Coverage::GetScript(size_t i) {
return ToApiHandle<debug::Script>(coverage_->at(i).script);
}
debug::Coverage::Range debug::Coverage::GetRange(size_t i) {
return Range(&coverage_->at(i).toplevel, GetScript(i));
}
debug::Coverage debug::Coverage::Collect(Isolate* isolate) {
return Coverage(i::Coverage::Collect(reinterpret_cast<i::Isolate*>(isolate)));
}
void debug::Coverage::TogglePrecise(Isolate* isolate, bool enable) {
i::Coverage::TogglePrecise(reinterpret_cast<i::Isolate*>(isolate), enable);
}
const char* CpuProfileNode::GetFunctionNameStr() const { const char* CpuProfileNode::GetFunctionNameStr() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
return node->entry()->name(); return node->entry()->name();
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include "src/base/platform/time.h" #include "src/base/platform/time.h"
#include "src/base/sys-info.h" #include "src/base/sys-info.h"
#include "src/basic-block-profiler.h" #include "src/basic-block-profiler.h"
#include "src/debug/debug-coverage.h" #include "src/debug/debug-interface.h"
#include "src/interpreter/interpreter.h" #include "src/interpreter/interpreter.h"
#include "src/list-inl.h" #include "src/list-inl.h"
#include "src/msan.h" #include "src/msan.h"
...@@ -1658,38 +1658,38 @@ void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) { ...@@ -1658,38 +1658,38 @@ void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
namespace { namespace {
void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines, void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines,
i::Handle<i::Script> script, const i::Coverage::Range* range) { debug::Coverage::Range range) {
// Compute line and column numbers from source position. // Ensure space in the array.
i::Script::PositionInfo start; lines->resize(std::max(static_cast<size_t>(range.End().GetLineNumber() + 1),
i::Script::PositionInfo end; lines->size()),
i::Script::GetPositionInfo(script, range->start, &start, 0);
i::Script::NO_OFFSET);
i::Script::GetPositionInfo(script, range->end, &end, i::Script::NO_OFFSET);
// Boundary lines could be shared between two functions with different // Boundary lines could be shared between two functions with different
// invocation counts. Take the maximum. // invocation counts. Take the maximum.
lines->at(start.line) = std::max(lines->at(start.line), range->count); lines->at(range.Start().GetLineNumber()) =
lines->at(end.line) = std::max(lines->at(end.line), range->count); std::max(lines->at(range.Start().GetLineNumber()), range.Count());
lines->at(range.End().GetLineNumber()) =
std::max(lines->at(range.End().GetLineNumber()), range.Count());
// Invocation counts for non-boundary lines are overwritten. // Invocation counts for non-boundary lines are overwritten.
for (int i = start.line + 1; i < end.line; i++) lines->at(i) = range->count; int line_plus_one = range.Start().GetLineNumber() + 1;
for (int i = line_plus_one; i < range.End().GetLineNumber(); i++) {
lines->at(i) = range.Count();
}
// Note that we use 0-based line numbers. But LCOV uses 1-based line numbers. // Note that we use 0-based line numbers. But LCOV uses 1-based line numbers.
if (!range->name.empty()) { // Recurse over inner ranges.
// Truncate UC16 characters down to Latin1. for (size_t i = 0; i < range.NestedCount(); i++) {
std::unique_ptr<char[]> name(new char[range->name.size() + 1]); ReadRange(s, lines, range.GetNested(i));
for (size_t i = 0; i < range->name.size(); i++) { }
name[i] = static_cast<char>(range->name.data()[i]); // Write function stats.
} Local<String> name;
name[range->name.size()] = 0; std::stringstream name_stream;
*s << "FN:" << (start.line + 1) << "," << name.get() << std::endl; if (range.Name().ToLocal(&name)) {
*s << "FNDA:" << range->count << "," << name.get() << std::endl; name_stream << ToSTLString(name);
} else { } else {
// Anonymous function. Use line and column as name. name_stream << "<" << line_plus_one << "-";
*s << "FN:" << (start.line + 1) << ","; name_stream << range.Start().GetColumnNumber() << ">";
*s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl;
*s << "FNDA:" << range->count << ",";
*s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl;
} }
// Recurse over inner ranges. *s << "FN:" << line_plus_one << "," << name_stream.str() << std::endl;
for (const auto& inner : range->inner) ReadRange(s, lines, script, &inner); *s << "FNDA:" << range.Count() << "," << name_stream.str() << std::endl;
} }
} // anonymous namespace } // anonymous namespace
...@@ -1697,22 +1697,20 @@ void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines, ...@@ -1697,22 +1697,20 @@ void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines,
void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) { void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
if (!file) return; if (!file) return;
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
std::vector<i::Coverage::ScriptData> data = debug::Coverage coverage = debug::Coverage::Collect(isolate);
i::Coverage::Collect(reinterpret_cast<i::Isolate*>(isolate));
std::ofstream sink(file, std::ofstream::app); std::ofstream sink(file, std::ofstream::app);
for (const auto& script_data : data) { for (size_t i = 0; i < coverage.ScriptCount(); i++) {
i::Handle<i::Script> script = script_data.script; Local<debug::Script> script = coverage.GetScript(i);
// Skip unnamed scripts. // Skip unnamed scripts.
if (!script->name()->IsString()) continue; Local<String> name;
std::string file_name = ToSTLString(v8::Utils::ToLocal( if (!script->Name().ToLocal(&name)) continue;
i::Handle<i::String>(i::String::cast(script->name())))); std::string file_name = ToSTLString(name);
// Skip scripts not backed by a file. // Skip scripts not backed by a file.
if (!std::ifstream(file_name).good()) continue; if (!std::ifstream(file_name).good()) continue;
sink << "SF:"; sink << "SF:";
sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl; sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
std::vector<uint32_t> lines; std::vector<uint32_t> lines;
lines.resize(i::FixedArray::cast(script->line_ends())->length(), 0); ReadRange(&sink, &lines, coverage.GetRange(i));
ReadRange(&sink, &lines, script, &script_data.toplevel);
// Write per-line coverage. LCOV uses 1-based line numbers. // Write per-line coverage. LCOV uses 1-based line numbers.
for (size_t i = 0; i < lines.size(); i++) { for (size_t i = 0; i < lines.size(); i++) {
sink << "DA:" << (i + 1) << "," << lines[i] << std::endl; sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
...@@ -2513,9 +2511,7 @@ int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) { ...@@ -2513,9 +2511,7 @@ int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
options.isolate_sources[i].StartExecuteInThread(); options.isolate_sources[i].StartExecuteInThread();
} }
{ {
if (options.lcov_file) { if (options.lcov_file) debug::Coverage::TogglePrecise(isolate, true);
i::Coverage::EnablePrecise(reinterpret_cast<i::Isolate*>(isolate));
}
HandleScope scope(isolate); HandleScope scope(isolate);
Local<Context> context = CreateEvaluationContext(isolate); Local<Context> context = CreateEvaluationContext(isolate);
if (last_run && options.use_interactive_shell()) { if (last_run && options.use_interactive_shell()) {
......
...@@ -58,7 +58,12 @@ bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) { ...@@ -58,7 +58,12 @@ bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
} }
} // anonymous namespace } // anonymous namespace
std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { CoverageScript::CoverageScript(Isolate* isolate, Handle<Script> s,
int source_length)
: script(s),
toplevel(0, source_length, 1, isolate->factory()->empty_string()) {}
Coverage* Coverage::Collect(Isolate* isolate) {
SharedToCounterMap counter_map; SharedToCounterMap counter_map;
// Feed invocation count into the counter map. // Feed invocation count into the counter map.
...@@ -89,15 +94,16 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { ...@@ -89,15 +94,16 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) {
// Iterate shared function infos of every script and build a mapping // Iterate shared function infos of every script and build a mapping
// between source ranges and invocation counts. // between source ranges and invocation counts.
std::vector<Coverage::ScriptData> result; Coverage* result = new Coverage();
Script::Iterator scripts(isolate); Script::Iterator scripts(isolate);
while (Script* script = scripts.Next()) { while (Script* script = scripts.Next()) {
// Dismiss non-user scripts. // Dismiss non-user scripts.
if (script->type() != Script::TYPE_NORMAL) continue; if (script->type() != Script::TYPE_NORMAL) continue;
// Create and add new script data. // Create and add new script data.
int source_length = String::cast(script->source())->length(); int source_end = String::cast(script->source())->length();
result.emplace_back(Handle<Script>(script, isolate), source_length); Handle<Script> script_handle(script, isolate);
result->emplace_back(isolate, script_handle, source_end);
std::vector<SharedFunctionInfo*> sorted; std::vector<SharedFunctionInfo*> sorted;
...@@ -105,14 +111,13 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { ...@@ -105,14 +111,13 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) {
// Collect a list of shared function infos sorted by start position. // Collect a list of shared function infos sorted by start position.
// Shared function infos are usually already sorted. Except for classes. // Shared function infos are usually already sorted. Except for classes.
// If the start position is the same, sort from outer to inner function. // If the start position is the same, sort from outer to inner function.
HandleScope scope(isolate); SharedFunctionInfo::ScriptIterator infos(script_handle);
SharedFunctionInfo::ScriptIterator infos(Handle<Script>(script, isolate));
while (SharedFunctionInfo* info = infos.Next()) sorted.push_back(info); while (SharedFunctionInfo* info = infos.Next()) sorted.push_back(info);
std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo); std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo);
} }
std::vector<Range*> stack; std::vector<CoverageRange*> stack;
stack.push_back(&result.back().toplevel); stack.push_back(&result->back().toplevel);
// Use sorted list to reconstruct function nesting. // Use sorted list to reconstruct function nesting.
for (SharedFunctionInfo* info : sorted) { for (SharedFunctionInfo* info : sorted) {
...@@ -122,30 +127,28 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) { ...@@ -122,30 +127,28 @@ std::vector<Coverage::ScriptData> Coverage::Collect(Isolate* isolate) {
if (info->is_toplevel()) { if (info->is_toplevel()) {
// Top-level function is available. // Top-level function is available.
DCHECK_EQ(1, stack.size()); DCHECK_EQ(1, stack.size());
result.back().toplevel.start = start; result->back().toplevel.start = start;
result.back().toplevel.end = end; result->back().toplevel.end = end;
result.back().toplevel.count = count; result->back().toplevel.count = count;
} else { } else {
// The shared function infos are sorted by start. // The shared function infos are sorted by start.
DCHECK_LE(stack.back()->start, start); DCHECK_LE(stack.back()->start, start);
// Drop the stack to the outer function. // Drop the stack to the outer function.
while (start >= stack.back()->end) stack.pop_back(); while (start >= stack.back()->end) stack.pop_back();
Range* outer = stack.back(); CoverageRange* outer = stack.back();
// New nested function. // New nested function.
DCHECK_LE(end, outer->end); DCHECK_LE(end, outer->end);
outer->inner.emplace_back(start, end, count); Handle<String> name(info->DebugName(), isolate);
Range& nested = outer->inner.back(); outer->inner.emplace_back(start, end, count, name);
String* name = info->DebugName(); stack.push_back(&outer->inner.back());
nested.name.resize(name->length());
String::WriteToFlat(name, nested.name.data(), 0, name->length());
stack.push_back(&nested);
} }
} }
} }
return result; return result;
} }
void Coverage::EnablePrecise(Isolate* isolate) { void Coverage::TogglePrecise(Isolate* isolate, bool enable) {
if (enable) {
HandleScope scope(isolate); HandleScope scope(isolate);
// Remove all optimized function. Optimized and inlined functions do not // Remove all optimized function. Optimized and inlined functions do not
// increment invocation count. // increment invocation count.
...@@ -168,10 +171,9 @@ void Coverage::EnablePrecise(Isolate* isolate) { ...@@ -168,10 +171,9 @@ void Coverage::EnablePrecise(Isolate* isolate) {
ArrayList::New(isolate, static_cast<int>(vectors.size())); ArrayList::New(isolate, static_cast<int>(vectors.size()));
for (const auto& vector : vectors) list = ArrayList::Add(list, vector); for (const auto& vector : vectors) list = ArrayList::Add(list, vector);
isolate->SetCodeCoverageList(*list); isolate->SetCodeCoverageList(*list);
} } else {
void Coverage::DisablePrecise(Isolate* isolate) {
isolate->SetCodeCoverageList(isolate->heap()->undefined_value()); isolate->SetCodeCoverageList(isolate->heap()->undefined_value());
}
} }
} // namespace internal } // namespace internal
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include <vector> #include <vector>
#include "src/allocation.h"
#include "src/debug/debug-interface.h" #include "src/debug/debug-interface.h"
#include "src/objects.h" #include "src/objects.h"
...@@ -17,31 +16,35 @@ namespace internal { ...@@ -17,31 +16,35 @@ namespace internal {
// Forward declaration. // Forward declaration.
class Isolate; class Isolate;
class Coverage : public AllStatic { struct CoverageRange {
public: CoverageRange(int s, int e, uint32_t c, Handle<String> n)
struct Range { : start(s), end(e), count(c), name(n) {}
Range(int s, int e, uint32_t c) : start(s), end(e), count(c) {}
int start; int start;
int end; int end;
uint32_t count; uint32_t count;
std::vector<uint16_t> name; Handle<String> name;
std::vector<Range> inner; std::vector<CoverageRange> inner;
}; };
struct ScriptData { struct CoverageScript {
// Initialize top-level function in case it has been garbage-collected. // Initialize top-level function in case it has been garbage-collected.
ScriptData(Handle<Script> s, int source_length) CoverageScript(Isolate* isolate, Handle<Script> s, int source_length);
: script(s), toplevel(0, source_length, 1) {}
Handle<Script> script; Handle<Script> script;
Range toplevel; CoverageRange toplevel;
}; };
V8_EXPORT_PRIVATE static std::vector<ScriptData> Collect(Isolate* isolate); class Coverage : public std::vector<CoverageScript> {
public:
// Allocate a new Coverage object and populate with result.
// The ownership is transferred to the caller.
static Coverage* Collect(Isolate* isolate);
// Enable precise code coverage. This disables optimization and makes sure // Enable precise code coverage. This disables optimization and makes sure
// invocation count is not affected by GC. // invocation count is not affected by GC.
V8_EXPORT_PRIVATE static void EnablePrecise(Isolate* isolate); static void TogglePrecise(Isolate* isolate, bool enable);
V8_EXPORT_PRIVATE static void DisablePrecise(Isolate* isolate);
private:
Coverage() {}
}; };
} // namespace internal } // namespace internal
......
...@@ -12,8 +12,16 @@ ...@@ -12,8 +12,16 @@
#include "include/v8.h" #include "include/v8.h"
#include "src/debug/interface-types.h" #include "src/debug/interface-types.h"
#include "src/globals.h"
namespace v8 { namespace v8 {
namespace internal {
struct CoverageRange;
class Coverage;
class Script;
}
namespace debug { namespace debug {
/** /**
...@@ -109,7 +117,7 @@ void SetOutOfMemoryCallback(Isolate* isolate, OutOfMemoryCallback callback, ...@@ -109,7 +117,7 @@ void SetOutOfMemoryCallback(Isolate* isolate, OutOfMemoryCallback callback,
/** /**
* Native wrapper around v8::internal::Script object. * Native wrapper around v8::internal::Script object.
*/ */
class Script { class V8_EXPORT_PRIVATE Script {
public: public:
v8::Isolate* GetIsolate() const; v8::Isolate* GetIsolate() const;
...@@ -198,6 +206,45 @@ class GeneratorObject { ...@@ -198,6 +206,45 @@ class GeneratorObject {
static v8::Local<debug::GeneratorObject> Cast(v8::Local<v8::Value> value); static v8::Local<debug::GeneratorObject> Cast(v8::Local<v8::Value> value);
}; };
/*
* Provide API layer between inspector and code coverage.
*/
class V8_EXPORT_PRIVATE Coverage {
public:
class V8_EXPORT_PRIVATE Range {
public:
// 0-based line and colum numbers.
Location Start() { return start_; }
Location End() { return end_; }
uint32_t Count();
size_t NestedCount();
Range GetNested(size_t i);
MaybeLocal<String> Name();
private:
Range(i::CoverageRange* range, Local<debug::Script> script);
i::CoverageRange* range_;
Location start_;
Location end_;
Local<debug::Script> script_;
friend class debug::Coverage;
};
static Coverage Collect(Isolate* isolate);
static void TogglePrecise(Isolate* isolate, bool enable);
size_t ScriptCount();
Local<debug::Script> GetScript(size_t i);
Range GetRange(size_t i);
~Coverage();
private:
explicit Coverage(i::Coverage* coverage) : coverage_(coverage) {}
i::Coverage* coverage_;
};
} // namespace debug } // namespace debug
} // namespace v8 } // namespace v8
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "src/globals.h"
namespace v8 { namespace v8 {
namespace debug { namespace debug {
...@@ -16,7 +18,7 @@ namespace debug { ...@@ -16,7 +18,7 @@ namespace debug {
* Defines location inside script. * Defines location inside script.
* Lines and columns are 0-based. * Lines and columns are 0-based.
*/ */
class Location { class V8_EXPORT_PRIVATE Location {
public: public:
Location(int line_number, int column_number); Location(int line_number, int column_number);
/** /**
......
...@@ -6694,10 +6694,8 @@ class Script: public Struct { ...@@ -6694,10 +6694,8 @@ class Script: public Struct {
// initializes the line ends array, avoiding expensive recomputations. // initializes the line ends array, avoiding expensive recomputations.
// The non-static version is not allocating and safe for unhandlified // The non-static version is not allocating and safe for unhandlified
// callsites. // callsites.
V8_EXPORT_PRIVATE static bool GetPositionInfo(Handle<Script> script, static bool GetPositionInfo(Handle<Script> script, int position,
int position, PositionInfo* info, OffsetFlag offset_flag);
PositionInfo* info,
OffsetFlag offset_flag);
bool GetPositionInfo(int position, PositionInfo* info, bool GetPositionInfo(int position, PositionInfo* info,
OffsetFlag offset_flag) const; OffsetFlag offset_flag) const;
......
...@@ -1895,8 +1895,7 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) { ...@@ -1895,8 +1895,7 @@ RUNTIME_FUNCTION(Runtime_DebugBreakInOptimizedCode) {
} }
namespace { namespace {
Handle<JSObject> CreateRangeObject(Isolate* isolate, Handle<JSObject> CreateRangeObject(Isolate* isolate, const CoverageRange* range,
const Coverage::Range* range,
Handle<String> inner_string, Handle<String> inner_string,
Handle<String> start_string, Handle<String> start_string,
Handle<String> end_string, Handle<String> end_string,
...@@ -1910,13 +1909,7 @@ Handle<JSObject> CreateRangeObject(Isolate* isolate, ...@@ -1910,13 +1909,7 @@ Handle<JSObject> CreateRangeObject(Isolate* isolate,
factory->NewNumberFromInt(range->end), NONE); factory->NewNumberFromInt(range->end), NONE);
JSObject::AddProperty(range_obj, count_string, JSObject::AddProperty(range_obj, count_string,
factory->NewNumberFromUint(range->count), NONE); factory->NewNumberFromUint(range->count), NONE);
Handle<String> name = factory->anonymous_string(); JSObject::AddProperty(range_obj, factory->name_string(), range->name, NONE);
if (!range->name.empty()) {
Vector<const uc16> name_vector(range->name.data(),
static_cast<int>(range->name.size()));
name = factory->NewStringFromTwoByte(name_vector).ToHandleChecked();
}
JSObject::AddProperty(range_obj, factory->name_string(), name, NONE);
if (!range->inner.empty()) { if (!range->inner.empty()) {
int size = static_cast<int>(range->inner.size()); int size = static_cast<int>(range->inner.size());
Handle<FixedArray> inner_array = factory->NewFixedArray(size); Handle<FixedArray> inner_array = factory->NewFixedArray(size);
...@@ -1937,11 +1930,11 @@ Handle<JSObject> CreateRangeObject(Isolate* isolate, ...@@ -1937,11 +1930,11 @@ Handle<JSObject> CreateRangeObject(Isolate* isolate,
RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) { RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) {
HandleScope scope(isolate); HandleScope scope(isolate);
// Collect coverage data. // Collect coverage data.
std::vector<Coverage::ScriptData> script_data = Coverage::Collect(isolate); std::unique_ptr<Coverage> coverage(Coverage::Collect(isolate));
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
// Turn the returned data structure into JavaScript. // Turn the returned data structure into JavaScript.
// Create an array of scripts. // Create an array of scripts.
int num_scripts = static_cast<int>(script_data.size()); int num_scripts = static_cast<int>(coverage->size());
// Prepare property keys. // Prepare property keys.
Handle<FixedArray> scripts_array = factory->NewFixedArray(num_scripts); Handle<FixedArray> scripts_array = factory->NewFixedArray(num_scripts);
Handle<String> script_string = factory->NewStringFromStaticChars("script"); Handle<String> script_string = factory->NewStringFromStaticChars("script");
...@@ -1952,7 +1945,7 @@ RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) { ...@@ -1952,7 +1945,7 @@ RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) {
Handle<String> end_string = factory->NewStringFromStaticChars("end"); Handle<String> end_string = factory->NewStringFromStaticChars("end");
Handle<String> count_string = factory->NewStringFromStaticChars("count"); Handle<String> count_string = factory->NewStringFromStaticChars("count");
for (int i = 0; i < num_scripts; i++) { for (int i = 0; i < num_scripts; i++) {
const auto& data = script_data[i]; const auto& data = coverage->at(i);
HandleScope inner_scope(isolate); HandleScope inner_scope(isolate);
Handle<JSObject> script_obj = factory->NewJSObjectWithNullProto(); Handle<JSObject> script_obj = factory->NewJSObjectWithNullProto();
Handle<JSObject> wrapper = Script::GetWrapper(data.script); Handle<JSObject> wrapper = Script::GetWrapper(data.script);
...@@ -1969,11 +1962,7 @@ RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) { ...@@ -1969,11 +1962,7 @@ RUNTIME_FUNCTION(Runtime_DebugCollectCoverage) {
RUNTIME_FUNCTION(Runtime_DebugTogglePreciseCoverage) { RUNTIME_FUNCTION(Runtime_DebugTogglePreciseCoverage) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
CONVERT_BOOLEAN_ARG_CHECKED(enable, 0); CONVERT_BOOLEAN_ARG_CHECKED(enable, 0);
if (enable) { Coverage::TogglePrecise(isolate, enable);
Coverage::EnablePrecise(isolate);
} else {
Coverage::DisablePrecise(isolate);
}
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -6624,3 +6624,40 @@ UNINITIALIZED_TEST(DebugSetOutOfMemoryListener) { ...@@ -6624,3 +6624,40 @@ UNINITIALIZED_TEST(DebugSetOutOfMemoryListener) {
} }
isolate->Dispose(); isolate->Dispose();
} }
TEST(DebugCoverage) {
i::FLAG_always_opt = false;
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::debug::Coverage::TogglePrecise(isolate, true);
v8::Local<v8::String> source = v8_str(
"function f() {\n"
"}\n"
"f();\n"
"f();");
CompileRun(source);
v8::debug::Coverage coverage = v8::debug::Coverage::Collect(isolate);
CHECK_EQ(1u, coverage.ScriptCount());
v8::Local<v8::debug::Script> script = coverage.GetScript(0);
CHECK(script->Source()
.ToLocalChecked()
->Equals(env.local(), source)
.FromMaybe(false));
v8::debug::Coverage::Range range = coverage.GetRange(0);
CHECK_EQ(0, range.Start().GetLineNumber());
CHECK_EQ(0, range.Start().GetColumnNumber());
CHECK_EQ(3, range.End().GetLineNumber());
CHECK_EQ(4, range.End().GetColumnNumber());
CHECK_EQ(1, range.Count());
CHECK_EQ(1u, range.NestedCount());
range = range.GetNested(0);
CHECK_EQ(0, range.Start().GetLineNumber());
CHECK_EQ(0, range.Start().GetColumnNumber());
CHECK_EQ(1, range.End().GetLineNumber());
CHECK_EQ(1, range.End().GetColumnNumber());
CHECK_EQ(2, range.Count());
CHECK_EQ(0, range.NestedCount());
}
...@@ -45,7 +45,7 @@ f(); ...@@ -45,7 +45,7 @@ f();
` `
[[function f() {}](f:2) [[function f() {}](f:2)
f(); f();
f();](anonymous:1) f();](:1)
` `
); );
...@@ -59,7 +59,7 @@ f(); ...@@ -59,7 +59,7 @@ f();
` `
[var f = [() => 1](f:2); [var f = [() => 1](f:2);
f(); f();
f();](anonymous:1) f();](:1)
` `
); );
...@@ -81,7 +81,7 @@ f(); ...@@ -81,7 +81,7 @@ f();
g(); g();
}](f:2) }](f:2)
f(); f();
f();](anonymous:1) f();](:1)
` `
); );
...@@ -100,6 +100,6 @@ fib(5); ...@@ -100,6 +100,6 @@ fib(5);
if (x < 2) return 1; if (x < 2) return 1;
return fib(x-1) + fib(x-2); return fib(x-1) + fib(x-2);
}](fib:15) }](fib:15)
fib(5);](anonymous:1) fib(5);](:1)
` `
); );
...@@ -70,7 +70,7 @@ TestCoverage( ...@@ -70,7 +70,7 @@ TestCoverage(
(function f() {})(); (function f() {})();
`, `,
` `
[([function f() {}](f:1))();](anonymous:1) [([function f() {}](f:1))();](:1)
` `
); );
...@@ -86,7 +86,7 @@ for (var i = 0; i < 10; i++) { ...@@ -86,7 +86,7 @@ for (var i = 0; i < 10; i++) {
[for (var i = 0; i < 10; i++) { [for (var i = 0; i < 10; i++) {
let f = [() => 1](f:5); let f = [() => 1](f:5);
i += f(); i += f();
}](anonymous:1) }](:1)
` `
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment