| Index: src/d8.cc
|
| diff --git a/src/d8.cc b/src/d8.cc
|
| index fcdf2b6e43e0a57f57520d904099e75a63ee7af0..e7e8c98343e5679f7c8550fe01e8a912433bb338 100644
|
| --- a/src/d8.cc
|
| +++ b/src/d8.cc
|
| @@ -30,6 +30,7 @@
|
| #include "src/base/platform/time.h"
|
| #include "src/base/sys-info.h"
|
| #include "src/basic-block-profiler.h"
|
| +#include "src/debug/debug-coverage.h"
|
| #include "src/interpreter/interpreter.h"
|
| #include "src/list-inl.h"
|
| #include "src/msan.h"
|
| @@ -1654,6 +1655,70 @@ void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
|
| JSON::Stringify(context, dispatch_counters).ToLocalChecked());
|
| }
|
|
|
| +namespace {
|
| +void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines,
|
| + i::Handle<i::Script> script, const i::Coverage::Range* range) {
|
| + // Compute line and column numbers from source position.
|
| + i::Script::PositionInfo start;
|
| + i::Script::PositionInfo end;
|
| + i::Script::GetPositionInfo(script, range->start, &start,
|
| + 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
|
| + // invocation counts. Take the maximum.
|
| + lines->at(start.line) = std::max(lines->at(start.line), range->count);
|
| + lines->at(end.line) = std::max(lines->at(end.line), range->count);
|
| + // Invocation counts for non-boundary lines are overwritten.
|
| + for (int i = start.line + 1; i < end.line; i++) lines->at(i) = range->count;
|
| + // Note that we use 0-based line numbers. But LCOV uses 1-based line numbers.
|
| + if (!range->name.empty()) {
|
| + // Truncate UC16 characters down to Latin1.
|
| + std::unique_ptr<char[]> name(new char[range->name.size() + 1]);
|
| + for (size_t i = 0; i < range->name.size(); i++) {
|
| + name[i] = static_cast<char>(range->name.data()[i]);
|
| + }
|
| + name[range->name.size()] = 0;
|
| + *s << "FN:" << (start.line + 1) << "," << name.get() << std::endl;
|
| + *s << "FNDA:" << range->count << "," << name.get() << std::endl;
|
| + } else {
|
| + // Anonymous function. Use line and column as name.
|
| + *s << "FN:" << (start.line + 1) << ",";
|
| + *s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl;
|
| + *s << "FNDA:" << range->count << ",";
|
| + *s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl;
|
| + }
|
| + // Recurse over inner ranges.
|
| + for (const auto& inner : range->inner) ReadRange(s, lines, script, &inner);
|
| +}
|
| +} // anonymous namespace
|
| +
|
| +// Write coverage data in LCOV format. See man page for geninfo(1).
|
| +void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
|
| + if (!file) return;
|
| + HandleScope handle_scope(isolate);
|
| + std::vector<i::Coverage::ScriptData> data =
|
| + i::Coverage::Collect(reinterpret_cast<i::Isolate*>(isolate));
|
| + std::ofstream sink(file, std::ofstream::app);
|
| + for (const auto& script_data : data) {
|
| + i::Handle<i::Script> script = script_data.script;
|
| + // Skip unnamed scripts.
|
| + if (!script->name()->IsString()) continue;
|
| + std::string file_name = ToSTLString(v8::Utils::ToLocal(
|
| + i::Handle<i::String>(i::String::cast(script->name()))));
|
| + // Skip scripts not backed by a file.
|
| + if (!std::ifstream(file_name).good()) continue;
|
| + sink << "SF:";
|
| + sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
|
| + std::vector<uint32_t> lines;
|
| + lines.resize(i::FixedArray::cast(script->line_ends())->length(), 0);
|
| + ReadRange(&sink, &lines, script, &script_data.toplevel);
|
| + // Write per-line coverage. LCOV uses 1-based line numbers.
|
| + for (size_t i = 0; i < lines.size(); i++) {
|
| + sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
|
| + }
|
| + sink << "end_of_record" << std::endl;
|
| + }
|
| +}
|
|
|
| void Shell::OnExit(v8::Isolate* isolate) {
|
| if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
|
| @@ -1714,7 +1779,6 @@ void Shell::OnExit(v8::Isolate* isolate) {
|
| }
|
|
|
|
|
| -
|
| static FILE* FOpen(const char* path, const char* mode) {
|
| #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
|
| FILE* result;
|
| @@ -2396,6 +2460,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
| } else if (strcmp(argv[i], "--enable-inspector") == 0) {
|
| options.enable_inspector = true;
|
| argv[i] = NULL;
|
| + } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
|
| + options.lcov_file = argv[i] + 7;
|
| + argv[i] = NULL;
|
| }
|
| }
|
|
|
| @@ -2437,6 +2504,9 @@ int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
|
| options.isolate_sources[i].StartExecuteInThread();
|
| }
|
| {
|
| + if (options.lcov_file) {
|
| + i::Coverage::EnablePrecise(reinterpret_cast<i::Isolate*>(isolate));
|
| + }
|
| HandleScope scope(isolate);
|
| Local<Context> context = CreateEvaluationContext(isolate);
|
| if (last_run && options.use_interactive_shell()) {
|
| @@ -2450,6 +2520,7 @@ int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
|
| options.isolate_sources[0].Execute(isolate);
|
| }
|
| DisposeModuleEmbedderData(context);
|
| + WriteLcovData(isolate, options.lcov_file);
|
| }
|
| CollectGarbage(isolate);
|
| for (int i = 1; i < options.num_isolates; ++i) {
|
|
|