LCOV - code coverage report
Current view: top level - src/inspector - wasm-translation.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 121 135 89.6 %
Date: 2017-10-20 Functions: 19 26 73.1 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/inspector/wasm-translation.h"
       6             : 
       7             : #include <algorithm>
       8             : 
       9             : #include "src/debug/debug-interface.h"
      10             : #include "src/inspector/string-util.h"
      11             : #include "src/inspector/v8-debugger-agent-impl.h"
      12             : #include "src/inspector/v8-debugger-script.h"
      13             : #include "src/inspector/v8-debugger.h"
      14             : #include "src/inspector/v8-inspector-impl.h"
      15             : 
      16             : namespace v8_inspector {
      17             : 
      18          49 : class WasmTranslation::TranslatorImpl {
      19             :  public:
      20             :   struct TransLocation {
      21             :     WasmTranslation* translation;
      22             :     String16 script_id;
      23             :     int line;
      24             :     int column;
      25             :     TransLocation(WasmTranslation* translation, String16 script_id, int line,
      26             :                   int column)
      27             :         : translation(translation),
      28             :           script_id(script_id),
      29             :           line(line),
      30         883 :           column(column) {}
      31             :   };
      32             : 
      33             :   virtual void Init(v8::Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) = 0;
      34             :   virtual void Translate(TransLocation*) = 0;
      35             :   virtual void TranslateBack(TransLocation*) = 0;
      36          49 :   virtual ~TranslatorImpl() {}
      37             : 
      38             :   class RawTranslator;
      39             :   class DisassemblingTranslator;
      40             : };
      41             : 
      42           0 : class WasmTranslation::TranslatorImpl::RawTranslator
      43             :     : public WasmTranslation::TranslatorImpl {
      44             :  public:
      45           0 :   void Init(v8::Isolate*, WasmTranslation*, V8DebuggerAgentImpl*) {}
      46           0 :   void Translate(TransLocation*) {}
      47           0 :   void TranslateBack(TransLocation*) {}
      48             : };
      49             : 
      50         147 : class WasmTranslation::TranslatorImpl::DisassemblingTranslator
      51             :     : public WasmTranslation::TranslatorImpl {
      52             :   using OffsetTable = v8::debug::WasmDisassembly::OffsetTable;
      53             : 
      54             :  public:
      55          49 :   DisassemblingTranslator(v8::Isolate* isolate,
      56             :                           v8::Local<v8::debug::WasmScript> script)
      57         147 :       : script_(isolate, script) {}
      58             : 
      59          49 :   void Init(v8::Isolate* isolate, WasmTranslation* translation,
      60             :             V8DebuggerAgentImpl* agent) override {
      61             :     // Register fake scripts for each function in this wasm module/script.
      62             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
      63          49 :     int num_functions = script->NumFunctions();
      64          49 :     int num_imported_functions = script->NumImportedFunctions();
      65             :     DCHECK_LE(0, num_imported_functions);
      66             :     DCHECK_LE(0, num_functions);
      67             :     DCHECK_GE(num_functions, num_imported_functions);
      68          49 :     String16 script_id = String16::fromInteger(script->Id());
      69         132 :     for (int func_idx = num_imported_functions; func_idx < num_functions;
      70             :          ++func_idx) {
      71          83 :       AddFakeScript(isolate, script_id, func_idx, translation, agent);
      72             :     }
      73          49 :   }
      74             : 
      75         763 :   void Translate(TransLocation* loc) override {
      76        2289 :     const OffsetTable& offset_table = GetOffsetTable(loc);
      77             :     DCHECK(!offset_table.empty());
      78         763 :     uint32_t byte_offset = static_cast<uint32_t>(loc->column);
      79             : 
      80             :     // Binary search for the given offset.
      81             :     unsigned left = 0;                                            // inclusive
      82         763 :     unsigned right = static_cast<unsigned>(offset_table.size());  // exclusive
      83        3683 :     while (right - left > 1) {
      84        2157 :       unsigned mid = (left + right) / 2;
      85        4314 :       if (offset_table[mid].byte_offset <= byte_offset) {
      86             :         left = mid;
      87             :       } else {
      88             :         right = mid;
      89             :       }
      90             :     }
      91             : 
      92        1526 :     loc->script_id = GetFakeScriptId(loc);
      93        1526 :     if (offset_table[left].byte_offset == byte_offset) {
      94         763 :       loc->line = offset_table[left].line;
      95         763 :       loc->column = offset_table[left].column;
      96             :     } else {
      97           0 :       loc->line = 0;
      98           0 :       loc->column = 0;
      99             :     }
     100         763 :   }
     101             : 
     102         120 :   void TranslateBack(TransLocation* loc) override {
     103         120 :     int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
     104         240 :     const OffsetTable* reverse_table = GetReverseTable(func_index);
     105         240 :     if (!reverse_table) return;
     106             :     DCHECK(!reverse_table->empty());
     107         120 :     v8::Isolate* isolate = loc->translation->isolate_;
     108             : 
     109             :     // Binary search for the given line and column.
     110             :     unsigned left = 0;                                              // inclusive
     111         120 :     unsigned right = static_cast<unsigned>(reverse_table->size());  // exclusive
     112         535 :     while (right - left > 1) {
     113         295 :       unsigned mid = (left + right) / 2;
     114         295 :       auto& entry = (*reverse_table)[mid];
     115         295 :       if (entry.line < loc->line ||
     116          75 :           (entry.line == loc->line && entry.column <= loc->column)) {
     117             :         left = mid;
     118             :       } else {
     119             :         right = mid;
     120             :       }
     121             :     }
     122             : 
     123             :     int found_byte_offset = 0;
     124             :     // [left] is <= <line,column>, or left==0 and [0] > <line,column>.
     125             :     // We are searching for the smallest entry >= <line,column> which is still
     126             :     // on the same line. This must be either [left] or [left + 1].
     127             :     // If we don't find such an entry, we might have hit the special case of
     128             :     // pointing after the last line, which is translated to the end of the
     129             :     // function (one byte after the last function byte).
     130         305 :     if ((*reverse_table)[left].line == loc->line &&
     131          65 :         (*reverse_table)[left].column >= loc->column) {
     132          65 :       found_byte_offset = (*reverse_table)[left].byte_offset;
     133         160 :     } else if (left + 1 < reverse_table->size() &&
     134         135 :                (*reverse_table)[left + 1].line == loc->line &&
     135          30 :                (*reverse_table)[left + 1].column >= loc->column) {
     136          30 :       found_byte_offset = (*reverse_table)[left + 1].byte_offset;
     137          55 :     } else if (left == reverse_table->size() - 1 &&
     138          30 :                (*reverse_table)[left].line == loc->line - 1 &&
     139           5 :                loc->column == 0) {
     140             :       std::pair<int, int> func_range =
     141           5 :           script_.Get(isolate)->GetFunctionRange(func_index);
     142             :       DCHECK_LE(func_range.first, func_range.second);
     143           5 :       found_byte_offset = func_range.second - func_range.first;
     144             :     }
     145             : 
     146         240 :     loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
     147         120 :     loc->line = func_index;
     148         120 :     loc->column = found_byte_offset;
     149             :   }
     150             : 
     151             :  private:
     152          83 :   String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
     153             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     154         166 :     String16 script_name = toProtocolString(script->Name().ToLocalChecked());
     155          83 :     int numFunctions = script->NumFunctions();
     156          83 :     int numImported = script->NumImportedFunctions();
     157          83 :     String16Builder builder;
     158         166 :     builder.appendAll("wasm://wasm/", script_name, '/');
     159          83 :     if (numFunctions - numImported > 300) {
     160           0 :       size_t digits = String16::fromInteger(numFunctions - 1).length();
     161           0 :       String16 thisCategory = String16::fromInteger((func_index / 100) * 100);
     162             :       DCHECK_LE(thisCategory.length(), digits);
     163           0 :       for (size_t i = thisCategory.length(); i < digits; ++i)
     164           0 :         builder.append('0');
     165           0 :       builder.appendAll(thisCategory, '/');
     166             :     }
     167         166 :     builder.appendAll(script_name, '-');
     168          83 :     builder.appendNumber(func_index);
     169         166 :     return builder.toString();
     170             :   }
     171             : 
     172         846 :   String16 GetFakeScriptId(const String16 script_id, int func_index) {
     173        2538 :     return String16::concat(script_id, '-', String16::fromInteger(func_index));
     174             :   }
     175         763 :   String16 GetFakeScriptId(const TransLocation* loc) {
     176        1526 :     return GetFakeScriptId(loc->script_id, loc->line);
     177             :   }
     178             : 
     179          83 :   void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId,
     180             :                      int func_idx, WasmTranslation* translation,
     181             :                      V8DebuggerAgentImpl* agent) {
     182         166 :     String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
     183          83 :     String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
     184             : 
     185             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     186             :     // TODO(clemensh): Generate disassembly lazily when queried by the frontend.
     187             :     v8::debug::WasmDisassembly disassembly =
     188         166 :         script->DisassembleFunction(func_idx);
     189             : 
     190             :     DCHECK_EQ(0, offset_tables_.count(func_idx));
     191             :     offset_tables_.insert(
     192          83 :         std::make_pair(func_idx, std::move(disassembly.offset_table)));
     193             :     String16 source(disassembly.disassembly.data(),
     194          83 :                     disassembly.disassembly.length());
     195             :     std::unique_ptr<V8DebuggerScript> fake_script =
     196             :         V8DebuggerScript::CreateWasm(isolate, translation, script,
     197             :                                      fake_script_id, std::move(fake_script_url),
     198         332 :                                      source);
     199             : 
     200         166 :     translation->AddFakeScript(fake_script->scriptId(), this);
     201         166 :     agent->didParseSource(std::move(fake_script), true);
     202          83 :   }
     203             : 
     204         120 :   int GetFunctionIndexFromFakeScriptId(const String16& fake_script_id) {
     205             :     size_t last_dash_pos = fake_script_id.reverseFind('-');
     206             :     DCHECK_GT(fake_script_id.length(), last_dash_pos);
     207         120 :     bool ok = true;
     208         240 :     int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok);
     209             :     DCHECK(ok);
     210         120 :     return func_index;
     211             :   }
     212             : 
     213             :   const OffsetTable& GetOffsetTable(const TransLocation* loc) {
     214         763 :     int func_index = loc->line;
     215             :     auto it = offset_tables_.find(func_index);
     216             :     // TODO(clemensh): Once we load disassembly lazily, the offset table
     217             :     // might not be there yet. Load it lazily then.
     218             :     DCHECK(it != offset_tables_.end());
     219             :     return it->second;
     220             :   }
     221             : 
     222         120 :   const OffsetTable* GetReverseTable(int func_index) {
     223             :     auto it = reverse_tables_.find(func_index);
     224         120 :     if (it != reverse_tables_.end()) return &it->second;
     225             : 
     226             :     // Find offset table, copy and sort it to get reverse table.
     227             :     it = offset_tables_.find(func_index);
     228          25 :     if (it == offset_tables_.end()) return nullptr;
     229             : 
     230          25 :     OffsetTable reverse_table = it->second;
     231             :     // Order by line, column, then byte offset.
     232             :     auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) {
     233         310 :       if (el1.line != el2.line) return el1.line < el2.line;
     234           0 :       if (el1.column != el2.column) return el1.column < el2.column;
     235           0 :       return el1.byte_offset < el2.byte_offset;
     236             :     };
     237          25 :     std::sort(reverse_table.begin(), reverse_table.end(), cmp);
     238             : 
     239             :     auto inserted = reverse_tables_.insert(
     240          25 :         std::make_pair(func_index, std::move(reverse_table)));
     241             :     DCHECK(inserted.second);
     242          25 :     return &inserted.first->second;
     243             :   }
     244             : 
     245             :   v8::Global<v8::debug::WasmScript> script_;
     246             : 
     247             :   // We assume to only disassemble a subset of the functions, so store them in a
     248             :   // map instead of an array.
     249             :   std::unordered_map<int, const OffsetTable> offset_tables_;
     250             :   std::unordered_map<int, const OffsetTable> reverse_tables_;
     251             : };
     252             : 
     253        3261 : WasmTranslation::WasmTranslation(v8::Isolate* isolate)
     254        9783 :     : isolate_(isolate), mode_(Disassemble) {}
     255             : 
     256        6522 : WasmTranslation::~WasmTranslation() { Clear(); }
     257             : 
     258          49 : void WasmTranslation::AddScript(v8::Local<v8::debug::WasmScript> script,
     259             :                                 V8DebuggerAgentImpl* agent) {
     260             :   std::unique_ptr<TranslatorImpl> impl;
     261          49 :   switch (mode_) {
     262             :     case Raw:
     263           0 :       impl.reset(new TranslatorImpl::RawTranslator());
     264             :       break;
     265             :     case Disassemble:
     266          49 :       impl.reset(new TranslatorImpl::DisassemblingTranslator(isolate_, script));
     267          49 :       break;
     268             :   }
     269             :   DCHECK(impl);
     270             :   auto inserted =
     271          98 :       wasm_translators_.insert(std::make_pair(script->Id(), std::move(impl)));
     272             :   // Check that no mapping for this script id existed before.
     273             :   DCHECK(inserted.second);
     274             :   // impl has been moved, use the returned iterator to call Init.
     275          49 :   inserted.first->second->Init(isolate_, this, agent);
     276          49 : }
     277             : 
     278        3076 : void WasmTranslation::Clear() {
     279             :   wasm_translators_.clear();
     280             :   fake_scripts_.clear();
     281        3076 : }
     282             : 
     283             : // Translation "forward" (to artificial scripts).
     284      125513 : bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
     285             :     String16* script_id, int* line_number, int* column_number) {
     286             :   DCHECK(script_id && line_number && column_number);
     287      125513 :   bool ok = true;
     288      125513 :   int script_id_int = script_id->toInteger(&ok);
     289      125513 :   if (!ok) return false;
     290             : 
     291             :   auto it = wasm_translators_.find(script_id_int);
     292      125513 :   if (it == wasm_translators_.end()) return false;
     293             :   TranslatorImpl* translator = it->second.get();
     294             : 
     295             :   TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
     296        1526 :                                           *line_number, *column_number);
     297         763 :   translator->Translate(&trans_loc);
     298             : 
     299         763 :   *script_id = std::move(trans_loc.script_id);
     300         763 :   *line_number = trans_loc.line;
     301         763 :   *column_number = trans_loc.column;
     302             : 
     303             :   return true;
     304             : }
     305             : 
     306             : // Translation "backward" (from artificial to real scripts).
     307         120 : bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
     308             :     String16* script_id, int* line_number, int* column_number) {
     309             :   auto it = fake_scripts_.find(*script_id);
     310         120 :   if (it == fake_scripts_.end()) return false;
     311         120 :   TranslatorImpl* translator = it->second;
     312             : 
     313             :   TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
     314         240 :                                           *line_number, *column_number);
     315         120 :   translator->TranslateBack(&trans_loc);
     316             : 
     317         120 :   *script_id = std::move(trans_loc.script_id);
     318         120 :   *line_number = trans_loc.line;
     319         120 :   *column_number = trans_loc.column;
     320             : 
     321             :   return true;
     322             : }
     323             : 
     324          83 : void WasmTranslation::AddFakeScript(const String16& scriptId,
     325             :                                     TranslatorImpl* translator) {
     326             :   DCHECK_EQ(0, fake_scripts_.count(scriptId));
     327          83 :   fake_scripts_.insert(std::make_pair(scriptId, translator));
     328          83 : }
     329             : 
     330             : }  // namespace v8_inspector

Generated by: LCOV version 1.10