 Chromium Code Reviews
 Chromium Code Reviews Issue 2695823003:
  [debugger] add lcov support to d8.  (Closed)
    
  
    Issue 2695823003:
  [debugger] add lcov support to d8.  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include <errno.h> | 5 #include <errno.h> | 
| 6 #include <stdlib.h> | 6 #include <stdlib.h> | 
| 7 #include <string.h> | 7 #include <string.h> | 
| 8 #include <sys/stat.h> | 8 #include <sys/stat.h> | 
| 9 | 9 | 
| 10 #include <algorithm> | 10 #include <algorithm> | 
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 #include "include/libplatform/libplatform.h" | 23 #include "include/libplatform/libplatform.h" | 
| 24 #include "include/libplatform/v8-tracing.h" | 24 #include "include/libplatform/v8-tracing.h" | 
| 25 #include "src/api.h" | 25 #include "src/api.h" | 
| 26 #include "src/base/cpu.h" | 26 #include "src/base/cpu.h" | 
| 27 #include "src/base/debug/stack_trace.h" | 27 #include "src/base/debug/stack_trace.h" | 
| 28 #include "src/base/logging.h" | 28 #include "src/base/logging.h" | 
| 29 #include "src/base/platform/platform.h" | 29 #include "src/base/platform/platform.h" | 
| 30 #include "src/base/platform/time.h" | 30 #include "src/base/platform/time.h" | 
| 31 #include "src/base/sys-info.h" | 31 #include "src/base/sys-info.h" | 
| 32 #include "src/basic-block-profiler.h" | 32 #include "src/basic-block-profiler.h" | 
| 33 #include "src/debug/debug-coverage.h" | |
| 33 #include "src/interpreter/interpreter.h" | 34 #include "src/interpreter/interpreter.h" | 
| 34 #include "src/list-inl.h" | 35 #include "src/list-inl.h" | 
| 35 #include "src/msan.h" | 36 #include "src/msan.h" | 
| 36 #include "src/objects-inl.h" | 37 #include "src/objects-inl.h" | 
| 37 #include "src/snapshot/natives.h" | 38 #include "src/snapshot/natives.h" | 
| 38 #include "src/utils.h" | 39 #include "src/utils.h" | 
| 39 #include "src/v8.h" | 40 #include "src/v8.h" | 
| 40 | 41 | 
| 41 #ifdef V8_INSPECTOR_ENABLED | 42 #ifdef V8_INSPECTOR_ENABLED | 
| 42 #include "include/v8-inspector.h" | 43 #include "include/v8-inspector.h" | 
| (...skipping 1604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1647 | 1648 | 
| 1648 Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate) | 1649 Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate) | 
| 1649 ->interpreter() | 1650 ->interpreter() | 
| 1650 ->GetDispatchCountersObject(); | 1651 ->GetDispatchCountersObject(); | 
| 1651 std::ofstream dispatch_counters_stream( | 1652 std::ofstream dispatch_counters_stream( | 
| 1652 i::FLAG_trace_ignition_dispatches_output_file); | 1653 i::FLAG_trace_ignition_dispatches_output_file); | 
| 1653 dispatch_counters_stream << *String::Utf8Value( | 1654 dispatch_counters_stream << *String::Utf8Value( | 
| 1654 JSON::Stringify(context, dispatch_counters).ToLocalChecked()); | 1655 JSON::Stringify(context, dispatch_counters).ToLocalChecked()); | 
| 1655 } | 1656 } | 
| 1656 | 1657 | 
| 1658 #ifdef V8_OS_LINUX | |
| 1659 namespace { | |
| 1660 void ReadRange(std::ofstream* s, std::vector<uint32_t>* lines, | |
| 1661 i::Handle<i::Script> script, const i::Coverage::Range* range) { | |
| 1662 // Compute line and column numbers from source position. | |
| 1663 i::Script::PositionInfo start; | |
| 1664 i::Script::PositionInfo end; | |
| 1665 i::Script::GetPositionInfo(script, range->start, &start, | |
| 1666 i::Script::NO_OFFSET); | |
| 1667 i::Script::GetPositionInfo(script, range->end, &end, i::Script::NO_OFFSET); | |
| 1668 // Boundary lines could be shared between two functions with different | |
| 1669 // invocation counts. Take the maximum. | |
| 1670 lines->at(start.line) = std::max(lines->at(start.line), range->count); | |
| 1671 lines->at(end.line) = std::max(lines->at(end.line), range->count); | |
| 1672 // Invocation counts for non-boundary lines are overwritten. | |
| 1673 for (int i = start.line + 1; i < end.line; i++) lines->at(i) = range->count; | |
| 1674 // Note that we use 0-based line numbers. But LCOV uses 1-based line numbers. | |
| 1675 if (!range->name.empty()) { | |
| 1676 // Truncate UC16 characters down to Latin1. | |
| 1677 std::unique_ptr<char[]> name(new char[range->name.size() + 1]); | |
| 1678 for (size_t i = 0; i < range->name.size(); i++) { | |
| 1679 name[i] = static_cast<char>(range->name.data()[i]); | |
| 1680 } | |
| 1681 name[range->name.size()] = 0; | |
| 1682 *s << "FN:" << (start.line + 1) << "," << name.get() << std::endl; | |
| 1683 *s << "FNDA:" << range->count << "," << name.get() << std::endl; | |
| 1684 } else { | |
| 1685 // Anonymous function. Use line and column as name. | |
| 1686 *s << "FN:" << (start.line + 1) << ","; | |
| 1687 *s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl; | |
| 1688 *s << "FNDA:" << range->count << ","; | |
| 1689 *s << "<" << (start.line + 1) << "-" << start.column << ">" << std::endl; | |
| 1690 } | |
| 1691 // Recurse over inner ranges. | |
| 1692 for (const auto& inner : range->inner) ReadRange(s, lines, script, &inner); | |
| 1693 } | |
| 1694 } // anonymous namespace | |
| 1695 | |
| 1696 // Write coverage data in LCOV format. See man page for geninfo(1). | |
| 1697 void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) { | |
| 1698 if (!file) return; | |
| 1699 HandleScope handle_scope(isolate); | |
| 1700 const char* pwd = get_current_dir_name(); | |
| 1701 std::vector<i::Coverage::ScriptData> data = | |
| 1702 i::Coverage::Collect(reinterpret_cast<i::Isolate*>(isolate)); | |
| 1703 std::ofstream sink(file, std::ofstream::app); | |
| 1704 for (const auto& script_data : data) { | |
| 1705 i::Handle<i::Script> script = script_data.script; | |
| 1706 // Skip unnamed scripts. | |
| 1707 if (!script->name()->IsString()) continue; | |
| 1708 std::unique_ptr<char[]> name = i::String::cast(script->name())->ToCString(); | |
| 1709 // Skip scripts not backed by a file. | |
| 1710 if (!std::ifstream(name.get()).good()) continue; | |
| 1711 sink << "SF:"; | |
| 1712 // Prefix relative paths with current working directory. | |
| 1713 if (name.get()[0] != '/') sink << pwd << "/"; | |
| 1714 sink << name.get() << std::endl; | |
| 1715 i::Script::InitLineEnds(script); | |
| 1716 std::vector<uint32_t> lines; | |
| 1717 lines.resize(i::FixedArray::cast(script->line_ends())->length(), 0); | |
| 1718 ReadRange(&sink, &lines, script, &script_data.toplevel); | |
| 1719 // Write per-line coverage. LCOV uses 1-based line numbers. | |
| 1720 for (size_t i = 0; i < lines.size(); i++) { | |
| 1721 sink << "DA:" << (i + 1) << "," << lines[i] << std::endl; | |
| 1722 } | |
| 1723 sink << "end_of_record" << std::endl; | |
| 1724 } | |
| 1725 } | |
| 1726 #else | |
| 1727 void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {} | |
| 1728 #endif // V8_OS_LINUX | |
| 1657 | 1729 | 
| 1658 void Shell::OnExit(v8::Isolate* isolate) { | 1730 void Shell::OnExit(v8::Isolate* isolate) { | 
| 1659 if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) { | 1731 if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) { | 
| 1660 int number_of_counters = 0; | 1732 int number_of_counters = 0; | 
| 1661 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { | 1733 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { | 
| 1662 number_of_counters++; | 1734 number_of_counters++; | 
| 1663 } | 1735 } | 
| 1664 CounterAndKey* counters = new CounterAndKey[number_of_counters]; | 1736 CounterAndKey* counters = new CounterAndKey[number_of_counters]; | 
| 1665 int j = 0; | 1737 int j = 0; | 
| 1666 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) { | 1738 for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) { | 
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1707 "-------------+\n"); | 1779 "-------------+\n"); | 
| 1708 } | 1780 } | 
| 1709 delete [] counters; | 1781 delete [] counters; | 
| 1710 } | 1782 } | 
| 1711 | 1783 | 
| 1712 delete counters_file_; | 1784 delete counters_file_; | 
| 1713 delete counter_map_; | 1785 delete counter_map_; | 
| 1714 } | 1786 } | 
| 1715 | 1787 | 
| 1716 | 1788 | 
| 1717 | |
| 1718 static FILE* FOpen(const char* path, const char* mode) { | 1789 static FILE* FOpen(const char* path, const char* mode) { | 
| 1719 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) | 1790 #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64)) | 
| 1720 FILE* result; | 1791 FILE* result; | 
| 1721 if (fopen_s(&result, path, mode) == 0) { | 1792 if (fopen_s(&result, path, mode) == 0) { | 
| 1722 return result; | 1793 return result; | 
| 1723 } else { | 1794 } else { | 
| 1724 return NULL; | 1795 return NULL; | 
| 1725 } | 1796 } | 
| 1726 #else | 1797 #else | 
| 1727 FILE* file = fopen(path, mode); | 1798 FILE* file = fopen(path, mode); | 
| (...skipping 661 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2389 argv[i] = NULL; | 2460 argv[i] = NULL; | 
| 2390 } else if (strcmp(argv[i], "--enable-tracing") == 0) { | 2461 } else if (strcmp(argv[i], "--enable-tracing") == 0) { | 
| 2391 options.trace_enabled = true; | 2462 options.trace_enabled = true; | 
| 2392 argv[i] = NULL; | 2463 argv[i] = NULL; | 
| 2393 } else if (strncmp(argv[i], "--trace-config=", 15) == 0) { | 2464 } else if (strncmp(argv[i], "--trace-config=", 15) == 0) { | 
| 2394 options.trace_config = argv[i] + 15; | 2465 options.trace_config = argv[i] + 15; | 
| 2395 argv[i] = NULL; | 2466 argv[i] = NULL; | 
| 2396 } else if (strcmp(argv[i], "--enable-inspector") == 0) { | 2467 } else if (strcmp(argv[i], "--enable-inspector") == 0) { | 
| 2397 options.enable_inspector = true; | 2468 options.enable_inspector = true; | 
| 2398 argv[i] = NULL; | 2469 argv[i] = NULL; | 
| 2470 } 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
 | |
| 2471 options.lcov_file = argv[i] + 6; | |
| 2472 argv[i] = NULL; | |
| 2399 } | 2473 } | 
| 2400 } | 2474 } | 
| 2401 | 2475 | 
| 2402 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); | 2476 v8::V8::SetFlagsFromCommandLine(&argc, argv, true); | 
| 2403 | 2477 | 
| 2404 // Set up isolated source groups. | 2478 // Set up isolated source groups. | 
| 2405 options.isolate_sources = new SourceGroup[options.num_isolates]; | 2479 options.isolate_sources = new SourceGroup[options.num_isolates]; | 
| 2406 SourceGroup* current = options.isolate_sources; | 2480 SourceGroup* current = options.isolate_sources; | 
| 2407 current->Begin(argv, 1); | 2481 current->Begin(argv, 1); | 
| 2408 for (int i = 1; i < argc; i++) { | 2482 for (int i = 1; i < argc; i++) { | 
| (...skipping 21 matching lines...) Expand all Loading... | |
| 2430 | 2504 | 
| 2431 return true; | 2505 return true; | 
| 2432 } | 2506 } | 
| 2433 | 2507 | 
| 2434 | 2508 | 
| 2435 int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) { | 2509 int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) { | 
| 2436 for (int i = 1; i < options.num_isolates; ++i) { | 2510 for (int i = 1; i < options.num_isolates; ++i) { | 
| 2437 options.isolate_sources[i].StartExecuteInThread(); | 2511 options.isolate_sources[i].StartExecuteInThread(); | 
| 2438 } | 2512 } | 
| 2439 { | 2513 { | 
| 2514 if (options.lcov_file) { | |
| 2515 i::Coverage::EnablePrecise(reinterpret_cast<i::Isolate*>(isolate)); | |
| 2516 } | |
| 2440 HandleScope scope(isolate); | 2517 HandleScope scope(isolate); | 
| 2441 Local<Context> context = CreateEvaluationContext(isolate); | 2518 Local<Context> context = CreateEvaluationContext(isolate); | 
| 2442 if (last_run && options.use_interactive_shell()) { | 2519 if (last_run && options.use_interactive_shell()) { | 
| 2443 // Keep using the same context in the interactive shell. | 2520 // Keep using the same context in the interactive shell. | 
| 2444 evaluation_context_.Reset(isolate, context); | 2521 evaluation_context_.Reset(isolate, context); | 
| 2445 } | 2522 } | 
| 2446 { | 2523 { | 
| 2447 Context::Scope cscope(context); | 2524 Context::Scope cscope(context); | 
| 2448 InspectorClient inspector_client(context, options.enable_inspector); | 2525 InspectorClient inspector_client(context, options.enable_inspector); | 
| 2449 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); | 2526 PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); | 
| 2450 options.isolate_sources[0].Execute(isolate); | 2527 options.isolate_sources[0].Execute(isolate); | 
| 2451 } | 2528 } | 
| 2452 DisposeModuleEmbedderData(context); | 2529 DisposeModuleEmbedderData(context); | 
| 2530 WriteLcovData(isolate, options.lcov_file); | |
| 2453 } | 2531 } | 
| 2454 CollectGarbage(isolate); | 2532 CollectGarbage(isolate); | 
| 2455 for (int i = 1; i < options.num_isolates; ++i) { | 2533 for (int i = 1; i < options.num_isolates; ++i) { | 
| 2456 if (last_run) { | 2534 if (last_run) { | 
| 2457 options.isolate_sources[i].JoinThread(); | 2535 options.isolate_sources[i].JoinThread(); | 
| 2458 } else { | 2536 } else { | 
| 2459 options.isolate_sources[i].WaitForThread(); | 2537 options.isolate_sources[i].WaitForThread(); | 
| 2460 } | 2538 } | 
| 2461 } | 2539 } | 
| 2462 CleanupWorkers(); | 2540 CleanupWorkers(); | 
| (...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2928 } | 3006 } | 
| 2929 | 3007 | 
| 2930 } // namespace v8 | 3008 } // namespace v8 | 
| 2931 | 3009 | 
| 2932 | 3010 | 
| 2933 #ifndef GOOGLE3 | 3011 #ifndef GOOGLE3 | 
| 2934 int main(int argc, char* argv[]) { | 3012 int main(int argc, char* argv[]) { | 
| 2935 return v8::Shell::Main(argc, argv); | 3013 return v8::Shell::Main(argc, argv); | 
| 2936 } | 3014 } | 
| 2937 #endif | 3015 #endif | 
| OLD | NEW |