LCOV - code coverage report
Current view: top level - src/inspector - wasm-translation.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 136 148 91.9 %
Date: 2019-03-21 Functions: 27 28 96.4 %

          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             : #include <utility>
       9             : 
      10             : #include "src/debug/debug-interface.h"
      11             : #include "src/inspector/string-util.h"
      12             : #include "src/inspector/v8-debugger-agent-impl.h"
      13             : #include "src/inspector/v8-debugger-script.h"
      14             : #include "src/inspector/v8-debugger.h"
      15             : #include "src/inspector/v8-inspector-impl.h"
      16             : 
      17             : namespace v8_inspector {
      18             : 
      19             : using OffsetTable = v8::debug::WasmDisassembly::OffsetTable;
      20             : 
      21         684 : struct WasmSourceInformation {
      22             :   String16 source;
      23             :   int end_line = 0;
      24             :   int end_column = 0;
      25             : 
      26             :   OffsetTable offset_table;
      27             :   OffsetTable reverse_offset_table;
      28             : 
      29          76 :   WasmSourceInformation(String16 source, OffsetTable offset_table)
      30          76 :       : source(std::move(source)), offset_table(std::move(offset_table)) {
      31             :     int num_lines = 0;
      32             :     int last_newline = -1;
      33             :     size_t next_newline = this->source.find('\n', last_newline + 1);
      34         948 :     while (next_newline != String16::kNotFound) {
      35         436 :       last_newline = static_cast<int>(next_newline);
      36         436 :       next_newline = this->source.find('\n', last_newline + 1);
      37         436 :       ++num_lines;
      38             :     }
      39          76 :     end_line = num_lines;
      40          76 :     end_column = static_cast<int>(this->source.length()) - last_newline - 1;
      41             : 
      42          76 :     reverse_offset_table = this->offset_table;
      43             :     // Order by line, column, then byte offset.
      44             :     auto cmp = [](OffsetTable::value_type el1, OffsetTable::value_type el2) {
      45         552 :       if (el1.line != el2.line) return el1.line < el2.line;
      46           0 :       if (el1.column != el2.column) return el1.column < el2.column;
      47           0 :       return el1.byte_offset < el2.byte_offset;
      48             :     };
      49             :     std::sort(reverse_offset_table.begin(), reverse_offset_table.end(), cmp);
      50          76 :   }
      51             : 
      52             :   WasmSourceInformation() = default;
      53             : };
      54             : 
      55         104 : class WasmTranslation::TranslatorImpl {
      56             :  public:
      57         852 :   struct TransLocation {
      58             :     WasmTranslation* translation;
      59             :     String16 script_id;
      60             :     int line;
      61             :     int column;
      62             :     TransLocation(WasmTranslation* translation, String16 script_id, int line,
      63             :                   int column)
      64             :         : translation(translation),
      65             :           script_id(std::move(script_id)),
      66             :           line(line),
      67        1704 :           column(column) {}
      68             :   };
      69             : 
      70          52 :   TranslatorImpl(v8::Isolate* isolate, v8::Local<v8::debug::WasmScript> script)
      71             :       : script_(isolate, script) {
      72             :     script_.AnnotateStrongRetainer(kGlobalScriptHandleLabel);
      73          52 :   }
      74             : 
      75          52 :   void Init(v8::Isolate* isolate, WasmTranslation* translation,
      76             :             V8DebuggerAgentImpl* agent) {
      77             :     // Register fake scripts for each function in this wasm module/script.
      78             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
      79          52 :     int num_functions = script->NumFunctions();
      80          52 :     int num_imported_functions = script->NumImportedFunctions();
      81             :     DCHECK_LE(0, num_imported_functions);
      82             :     DCHECK_LE(0, num_functions);
      83             :     DCHECK_GE(num_functions, num_imported_functions);
      84          52 :     String16 script_id = String16::fromInteger(script->Id());
      85         220 :     for (int func_idx = num_imported_functions; func_idx < num_functions;
      86             :          ++func_idx) {
      87          84 :       AddFakeScript(isolate, script_id, func_idx, translation, agent);
      88             :     }
      89          52 :   }
      90             : 
      91         736 :   void Translate(TransLocation* loc) {
      92             :     const OffsetTable& offset_table = GetOffsetTable(loc);
      93             :     DCHECK(!offset_table.empty());
      94         736 :     uint32_t byte_offset = static_cast<uint32_t>(loc->column);
      95             : 
      96             :     // Binary search for the given offset.
      97             :     unsigned left = 0;                                            // inclusive
      98         736 :     unsigned right = static_cast<unsigned>(offset_table.size());  // exclusive
      99        2984 :     while (right - left > 1) {
     100        2248 :       unsigned mid = (left + right) / 2;
     101        4496 :       if (offset_table[mid].byte_offset <= byte_offset) {
     102             :         left = mid;
     103             :       } else {
     104             :         right = mid;
     105             :       }
     106             :     }
     107             : 
     108         736 :     loc->script_id = GetFakeScriptId(loc);
     109        1472 :     if (offset_table[left].byte_offset == byte_offset) {
     110         736 :       loc->line = offset_table[left].line;
     111         736 :       loc->column = offset_table[left].column;
     112             :     } else {
     113           0 :       loc->line = 0;
     114           0 :       loc->column = 0;
     115             :     }
     116         736 :   }
     117             : 
     118         356 :   static bool LessThan(const v8::debug::WasmDisassemblyOffsetTableEntry& entry,
     119             :                        const TransLocation& loc) {
     120         356 :     return entry.line < loc.line ||
     121          96 :            (entry.line == loc.line && entry.column < loc.column);
     122             :   }
     123             : 
     124         116 :   void TranslateBack(TransLocation* loc) {
     125         116 :     v8::Isolate* isolate = loc->translation->isolate_;
     126         116 :     int func_index = GetFunctionIndexFromFakeScriptId(loc->script_id);
     127             :     const OffsetTable& reverse_table = GetReverseTable(isolate, func_index);
     128         116 :     if (reverse_table.empty()) return;
     129             : 
     130             :     // Binary search for the given line and column.
     131             :     auto element = std::lower_bound(reverse_table.begin(), reverse_table.end(),
     132             :                                     *loc, LessThan);
     133             : 
     134             :     int found_byte_offset = 0;
     135             :     // We want an entry on the same line if possible.
     136         116 :     if (element == reverse_table.end()) {
     137             :       // We did not find an element, so this points after the function.
     138             :       std::pair<int, int> func_range =
     139           4 :           script_.Get(isolate)->GetFunctionRange(func_index);
     140             :       DCHECK_LE(func_range.first, func_range.second);
     141           4 :       found_byte_offset = func_range.second - func_range.first;
     142         112 :     } else if (element->line == loc->line || element == reverse_table.begin()) {
     143         112 :       found_byte_offset = element->byte_offset;
     144             :     } else {
     145             :       auto prev = element - 1;
     146             :       DCHECK(prev->line == loc->line);
     147           0 :       found_byte_offset = prev->byte_offset;
     148             :     }
     149             : 
     150         232 :     loc->script_id = String16::fromInteger(script_.Get(isolate)->Id());
     151         116 :     loc->line = func_index;
     152         116 :     loc->column = found_byte_offset;
     153             :   }
     154             : 
     155         980 :   const WasmSourceInformation& GetSourceInformation(v8::Isolate* isolate,
     156             :                                                     int index) {
     157             :     auto it = source_informations_.find(index);
     158         980 :     if (it != source_informations_.end()) return it->second;
     159         152 :     v8::HandleScope scope(isolate);
     160             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     161         152 :     v8::debug::WasmDisassembly disassembly = script->DisassembleFunction(index);
     162             : 
     163         304 :     auto inserted = source_informations_.insert(std::make_pair(
     164             :         index, WasmSourceInformation({disassembly.disassembly.data(),
     165             :                                       disassembly.disassembly.length()},
     166             :                                      std::move(disassembly.offset_table))));
     167             :     DCHECK(inserted.second);
     168          76 :     return inserted.first->second;
     169             :   }
     170             : 
     171          84 :   const String16 GetHash(v8::Isolate* isolate, int index) {
     172         168 :     v8::HandleScope scope(isolate);
     173             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     174          84 :     uint32_t hash = script->GetFunctionHash(index);
     175          84 :     String16Builder builder;
     176          84 :     builder.appendUnsignedAsHex(hash);
     177         168 :     return builder.toString();
     178             :   }
     179             : 
     180          16 :   int GetContextId(v8::Isolate* isolate) {
     181          32 :     v8::HandleScope scope(isolate);
     182             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     183          48 :     return script->ContextId().FromMaybe(0);
     184             :   }
     185             : 
     186             :  private:
     187          84 :   String16 GetFakeScriptUrl(v8::Isolate* isolate, int func_index) {
     188             :     v8::Local<v8::debug::WasmScript> script = script_.Get(isolate);
     189             :     String16 script_name =
     190         168 :         toProtocolString(isolate, script->Name().ToLocalChecked());
     191          84 :     int numFunctions = script->NumFunctions();
     192          84 :     int numImported = script->NumImportedFunctions();
     193          84 :     String16Builder builder;
     194         168 :     builder.appendAll("wasm://wasm/", script_name, '/');
     195          84 :     if (numFunctions - numImported > 300) {
     196           0 :       size_t digits = String16::fromInteger(numFunctions - 1).length();
     197           0 :       String16 thisCategory = String16::fromInteger((func_index / 100) * 100);
     198             :       DCHECK_LE(thisCategory.length(), digits);
     199           0 :       for (size_t i = thisCategory.length(); i < digits; ++i)
     200           0 :         builder.append('0');
     201           0 :       builder.appendAll(thisCategory, '/');
     202             :     }
     203          84 :     builder.appendAll(script_name, '-');
     204          84 :     builder.appendNumber(func_index);
     205         168 :     return builder.toString();
     206             :   }
     207             : 
     208         820 :   String16 GetFakeScriptId(const String16& script_id, int func_index) {
     209        3280 :     return String16::concat(script_id, '-', String16::fromInteger(func_index));
     210             :   }
     211             :   String16 GetFakeScriptId(const TransLocation* loc) {
     212         736 :     return GetFakeScriptId(loc->script_id, loc->line);
     213             :   }
     214             : 
     215          84 :   void AddFakeScript(v8::Isolate* isolate, const String16& underlyingScriptId,
     216             :                      int func_idx, WasmTranslation* translation,
     217             :                      V8DebuggerAgentImpl* agent) {
     218          84 :     String16 fake_script_id = GetFakeScriptId(underlyingScriptId, func_idx);
     219          84 :     String16 fake_script_url = GetFakeScriptUrl(isolate, func_idx);
     220             : 
     221             :     std::unique_ptr<V8DebuggerScript> fake_script =
     222             :         V8DebuggerScript::CreateWasm(isolate, translation, script_.Get(isolate),
     223             :                                      fake_script_id, std::move(fake_script_url),
     224         252 :                                      func_idx);
     225             : 
     226          84 :     translation->AddFakeScript(fake_script->scriptId(), this);
     227         168 :     agent->didParseSource(std::move(fake_script), true);
     228          84 :   }
     229             : 
     230         116 :   int GetFunctionIndexFromFakeScriptId(const String16& fake_script_id) {
     231             :     size_t last_dash_pos = fake_script_id.reverseFind('-');
     232             :     DCHECK_GT(fake_script_id.length(), last_dash_pos);
     233         116 :     bool ok = true;
     234         232 :     int func_index = fake_script_id.substring(last_dash_pos + 1).toInteger(&ok);
     235             :     DCHECK(ok);
     236         116 :     return func_index;
     237             :   }
     238             : 
     239             :   const OffsetTable& GetOffsetTable(const TransLocation* loc) {
     240             :     int func_index = loc->line;
     241         736 :     return GetSourceInformation(loc->translation->isolate_, func_index)
     242             :         .offset_table;
     243             :   }
     244             : 
     245             :   const OffsetTable& GetReverseTable(v8::Isolate* isolate, int func_index) {
     246         116 :     return GetSourceInformation(isolate, func_index).reverse_offset_table;
     247             :   }
     248             : 
     249             :   static constexpr char kGlobalScriptHandleLabel[] =
     250             :       "WasmTranslation::TranslatorImpl::script_";
     251             : 
     252             :   v8::Global<v8::debug::WasmScript> script_;
     253             : 
     254             :   // We assume to only disassemble a subset of the functions, so store them in a
     255             :   // map instead of an array.
     256             :   std::unordered_map<int, WasmSourceInformation> source_informations_;
     257             : };
     258             : 
     259             : constexpr char WasmTranslation::TranslatorImpl::kGlobalScriptHandleLabel[];
     260             : 
     261        7344 : WasmTranslation::WasmTranslation(v8::Isolate* isolate) : isolate_(isolate) {}
     262             : 
     263        7344 : WasmTranslation::~WasmTranslation() { Clear(); }
     264             : 
     265          52 : void WasmTranslation::AddScript(v8::Local<v8::debug::WasmScript> script,
     266             :                                 V8DebuggerAgentImpl* agent) {
     267         104 :   std::unique_ptr<TranslatorImpl> impl;
     268          52 :   impl.reset(new TranslatorImpl(isolate_, script));
     269             :   DCHECK(impl);
     270             :   auto inserted =
     271         104 :       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         104 :   inserted.first->second->Init(isolate_, this, agent);
     276          52 : }
     277             : 
     278        3413 : void WasmTranslation::Clear() {
     279             :   wasm_translators_.clear();
     280             :   fake_scripts_.clear();
     281        3413 : }
     282             : 
     283           4 : void WasmTranslation::Clear(v8::Isolate* isolate,
     284             :                             const std::vector<int>& contextIdsToClear) {
     285          20 :   for (auto iter = fake_scripts_.begin(); iter != fake_scripts_.end();) {
     286           8 :     auto contextId = iter->second->GetContextId(isolate);
     287             :     auto it = std::find(std::begin(contextIdsToClear),
     288           8 :                         std::end(contextIdsToClear), contextId);
     289           8 :     if (it != std::end(contextIdsToClear)) {
     290             :       iter = fake_scripts_.erase(iter);
     291             :     } else {
     292             :       ++iter;
     293             :     }
     294             :   }
     295             : 
     296          20 :   for (auto iter = wasm_translators_.begin();
     297             :        iter != wasm_translators_.end();) {
     298           8 :     auto contextId = iter->second->GetContextId(isolate);
     299             :     auto it = std::find(std::begin(contextIdsToClear),
     300           8 :                         std::end(contextIdsToClear), contextId);
     301           8 :     if (it != std::end(contextIdsToClear)) {
     302             :       iter = wasm_translators_.erase(iter);
     303             :     } else {
     304             :       ++iter;
     305             :     }
     306             :   }
     307           4 : }
     308             : 
     309          52 : const String16& WasmTranslation::GetSource(const String16& script_id,
     310             :                                            int func_index) {
     311             :   auto it = fake_scripts_.find(script_id);
     312             :   DCHECK_NE(it, fake_scripts_.end());
     313          52 :   return it->second->GetSourceInformation(isolate_, func_index).source;
     314             : }
     315             : 
     316          76 : int WasmTranslation::GetEndLine(const String16& script_id, int func_index) {
     317             :   auto it = fake_scripts_.find(script_id);
     318             :   DCHECK_NE(it, fake_scripts_.end());
     319          76 :   return it->second->GetSourceInformation(isolate_, func_index).end_line;
     320             : }
     321             : 
     322           0 : int WasmTranslation::GetEndColumn(const String16& script_id, int func_index) {
     323             :   auto it = fake_scripts_.find(script_id);
     324             :   DCHECK_NE(it, fake_scripts_.end());
     325           0 :   return it->second->GetSourceInformation(isolate_, func_index).end_column;
     326             : }
     327             : 
     328          84 : String16 WasmTranslation::GetHash(const String16& script_id, int func_index) {
     329             :   auto it = fake_scripts_.find(script_id);
     330             :   DCHECK_NE(it, fake_scripts_.end());
     331          84 :   return it->second->GetHash(isolate_, func_index);
     332             : }
     333             : 
     334             : // Translation "forward" (to artificial scripts).
     335      147609 : bool WasmTranslation::TranslateWasmScriptLocationToProtocolLocation(
     336             :     String16* script_id, int* line_number, int* column_number) {
     337             :   DCHECK(script_id && line_number && column_number);
     338      147609 :   bool ok = true;
     339      147609 :   int script_id_int = script_id->toInteger(&ok);
     340      147609 :   if (!ok) return false;
     341             : 
     342             :   auto it = wasm_translators_.find(script_id_int);
     343      147609 :   if (it == wasm_translators_.end()) return false;
     344             :   TranslatorImpl* translator = it->second.get();
     345             : 
     346             :   TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
     347        1472 :                                           *line_number, *column_number);
     348         736 :   translator->Translate(&trans_loc);
     349             : 
     350             :   *script_id = std::move(trans_loc.script_id);
     351         736 :   *line_number = trans_loc.line;
     352         736 :   *column_number = trans_loc.column;
     353             : 
     354             :   return true;
     355             : }
     356             : 
     357             : // Translation "backward" (from artificial to real scripts).
     358         116 : bool WasmTranslation::TranslateProtocolLocationToWasmScriptLocation(
     359             :     String16* script_id, int* line_number, int* column_number) {
     360             :   auto it = fake_scripts_.find(*script_id);
     361         116 :   if (it == fake_scripts_.end()) return false;
     362         116 :   TranslatorImpl* translator = it->second;
     363             : 
     364             :   TranslatorImpl::TransLocation trans_loc(this, std::move(*script_id),
     365         232 :                                           *line_number, *column_number);
     366         116 :   translator->TranslateBack(&trans_loc);
     367             : 
     368             :   *script_id = std::move(trans_loc.script_id);
     369         116 :   *line_number = trans_loc.line;
     370         116 :   *column_number = trans_loc.column;
     371             : 
     372             :   return true;
     373             : }
     374             : 
     375          84 : void WasmTranslation::AddFakeScript(const String16& scriptId,
     376             :                                     TranslatorImpl* translator) {
     377             :   DCHECK_EQ(0, fake_scripts_.count(scriptId));
     378          84 :   fake_scripts_.insert(std::make_pair(scriptId, translator));
     379          84 : }
     380             : }  // namespace v8_inspector

Generated by: LCOV version 1.10