Chromium Code Reviews| Index: src/d8.cc |
| diff --git a/src/d8.cc b/src/d8.cc |
| index fcdf2b6e43e0a57f57520d904099e75a63ee7af0..0638e6a89b247ade6091f725601929d57af46a8d 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,77 @@ void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) { |
| JSON::Stringify(context, dispatch_counters).ToLocalChecked()); |
| } |
| +#ifdef V8_OS_LINUX |
| +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); |
| + const char* pwd = get_current_dir_name(); |
| + 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::unique_ptr<char[]> name = i::String::cast(script->name())->ToCString(); |
| + // Skip scripts not backed by a file. |
| + if (!std::ifstream(name.get()).good()) continue; |
| + sink << "SF:"; |
| + // Prefix relative paths with current working directory. |
| + if (name.get()[0] != '/') sink << pwd << "/"; |
| + sink << name.get() << std::endl; |
| + i::Script::InitLineEnds(script); |
| + 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; |
| + } |
| +} |
| +#else |
| +void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {} |
| +#endif // V8_OS_LINUX |
| void Shell::OnExit(v8::Isolate* isolate) { |
| if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) { |
| @@ -1714,7 +1786,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 +2467,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=", 6) == 0) { |
|
jgruber
2017/02/15 08:20:23
Shouldn't this be 7 (to include the '=') here and
|
| + options.lcov_file = argv[i] + 6; |
| + argv[i] = NULL; |
| } |
| } |
| @@ -2437,6 +2511,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 +2527,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) { |