11cb0ef41Sopenharmony_ci// Copyright 2019 the V8 project authors. All rights reserved. Use of 21cb0ef41Sopenharmony_ci// this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#if !V8_ENABLE_WEBASSEMBLY 61cb0ef41Sopenharmony_ci#error This header should only be included if WebAssembly is enabled. 71cb0ef41Sopenharmony_ci#endif // !V8_ENABLE_WEBASSEMBLY 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci#ifndef V8_WASM_WASM_DEBUG_H_ 101cb0ef41Sopenharmony_ci#define V8_WASM_WASM_DEBUG_H_ 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ci#include <algorithm> 131cb0ef41Sopenharmony_ci#include <memory> 141cb0ef41Sopenharmony_ci#include <vector> 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci#include "include/v8-internal.h" 171cb0ef41Sopenharmony_ci#include "src/base/iterator.h" 181cb0ef41Sopenharmony_ci#include "src/base/logging.h" 191cb0ef41Sopenharmony_ci#include "src/base/macros.h" 201cb0ef41Sopenharmony_ci#include "src/base/vector.h" 211cb0ef41Sopenharmony_ci#include "src/wasm/value-type.h" 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_cinamespace v8 { 241cb0ef41Sopenharmony_cinamespace internal { 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_citemplate <typename T> 271cb0ef41Sopenharmony_ciclass Handle; 281cb0ef41Sopenharmony_ciclass WasmFrame; 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_cinamespace wasm { 311cb0ef41Sopenharmony_ci 321cb0ef41Sopenharmony_ciclass DebugInfoImpl; 331cb0ef41Sopenharmony_ciclass IndirectNameMap; 341cb0ef41Sopenharmony_ciclass NativeModule; 351cb0ef41Sopenharmony_ciclass WasmCode; 361cb0ef41Sopenharmony_ciclass WireBytesRef; 371cb0ef41Sopenharmony_ciclass WasmValue; 381cb0ef41Sopenharmony_cistruct WasmFunction; 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci// Side table storing information used to inspect Liftoff frames at runtime. 411cb0ef41Sopenharmony_ci// This table is only created on demand for debugging, so it is not optimized 421cb0ef41Sopenharmony_ci// for memory size. 431cb0ef41Sopenharmony_ciclass DebugSideTable { 441cb0ef41Sopenharmony_ci public: 451cb0ef41Sopenharmony_ci class Entry { 461cb0ef41Sopenharmony_ci public: 471cb0ef41Sopenharmony_ci enum Storage : int8_t { kConstant, kRegister, kStack }; 481cb0ef41Sopenharmony_ci struct Value { 491cb0ef41Sopenharmony_ci int index; 501cb0ef41Sopenharmony_ci ValueType type; 511cb0ef41Sopenharmony_ci Storage storage; 521cb0ef41Sopenharmony_ci union { 531cb0ef41Sopenharmony_ci int32_t i32_const; // if kind == kConstant 541cb0ef41Sopenharmony_ci int reg_code; // if kind == kRegister 551cb0ef41Sopenharmony_ci int stack_offset; // if kind == kStack 561cb0ef41Sopenharmony_ci }; 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ci bool operator==(const Value& other) const { 591cb0ef41Sopenharmony_ci if (index != other.index) return false; 601cb0ef41Sopenharmony_ci if (type != other.type) return false; 611cb0ef41Sopenharmony_ci if (storage != other.storage) return false; 621cb0ef41Sopenharmony_ci switch (storage) { 631cb0ef41Sopenharmony_ci case kConstant: 641cb0ef41Sopenharmony_ci return i32_const == other.i32_const; 651cb0ef41Sopenharmony_ci case kRegister: 661cb0ef41Sopenharmony_ci return reg_code == other.reg_code; 671cb0ef41Sopenharmony_ci case kStack: 681cb0ef41Sopenharmony_ci return stack_offset == other.stack_offset; 691cb0ef41Sopenharmony_ci } 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci bool operator!=(const Value& other) const { return !(*this == other); } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci bool is_constant() const { return storage == kConstant; } 741cb0ef41Sopenharmony_ci bool is_register() const { return storage == kRegister; } 751cb0ef41Sopenharmony_ci }; 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci Entry(int pc_offset, int stack_height, std::vector<Value> changed_values) 781cb0ef41Sopenharmony_ci : pc_offset_(pc_offset), 791cb0ef41Sopenharmony_ci stack_height_(stack_height), 801cb0ef41Sopenharmony_ci changed_values_(std::move(changed_values)) {} 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci // Constructor for map lookups (only initializes the {pc_offset_}). 831cb0ef41Sopenharmony_ci explicit Entry(int pc_offset) : pc_offset_(pc_offset) {} 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci int pc_offset() const { return pc_offset_; } 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci // Stack height, including locals. 881cb0ef41Sopenharmony_ci int stack_height() const { return stack_height_; } 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci base::Vector<const Value> changed_values() const { 911cb0ef41Sopenharmony_ci return base::VectorOf(changed_values_); 921cb0ef41Sopenharmony_ci } 931cb0ef41Sopenharmony_ci 941cb0ef41Sopenharmony_ci const Value* FindChangedValue(int stack_index) const { 951cb0ef41Sopenharmony_ci DCHECK_GT(stack_height_, stack_index); 961cb0ef41Sopenharmony_ci auto it = std::lower_bound( 971cb0ef41Sopenharmony_ci changed_values_.begin(), changed_values_.end(), stack_index, 981cb0ef41Sopenharmony_ci [](const Value& changed_value, int stack_index) { 991cb0ef41Sopenharmony_ci return changed_value.index < stack_index; 1001cb0ef41Sopenharmony_ci }); 1011cb0ef41Sopenharmony_ci return it != changed_values_.end() && it->index == stack_index ? &*it 1021cb0ef41Sopenharmony_ci : nullptr; 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci void Print(std::ostream&) const; 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci private: 1081cb0ef41Sopenharmony_ci int pc_offset_; 1091cb0ef41Sopenharmony_ci int stack_height_; 1101cb0ef41Sopenharmony_ci // Only store differences from the last entry, to keep the table small. 1111cb0ef41Sopenharmony_ci std::vector<Value> changed_values_; 1121cb0ef41Sopenharmony_ci }; 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci // Technically it would be fine to copy this class, but there should not be a 1151cb0ef41Sopenharmony_ci // reason to do so, hence mark it move only. 1161cb0ef41Sopenharmony_ci MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(DebugSideTable); 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ci explicit DebugSideTable(int num_locals, std::vector<Entry> entries) 1191cb0ef41Sopenharmony_ci : num_locals_(num_locals), entries_(std::move(entries)) { 1201cb0ef41Sopenharmony_ci DCHECK( 1211cb0ef41Sopenharmony_ci std::is_sorted(entries_.begin(), entries_.end(), EntryPositionLess{})); 1221cb0ef41Sopenharmony_ci } 1231cb0ef41Sopenharmony_ci 1241cb0ef41Sopenharmony_ci const Entry* GetEntry(int pc_offset) const { 1251cb0ef41Sopenharmony_ci auto it = std::lower_bound(entries_.begin(), entries_.end(), 1261cb0ef41Sopenharmony_ci Entry{pc_offset}, EntryPositionLess{}); 1271cb0ef41Sopenharmony_ci if (it == entries_.end() || it->pc_offset() != pc_offset) return nullptr; 1281cb0ef41Sopenharmony_ci DCHECK_LE(num_locals_, it->stack_height()); 1291cb0ef41Sopenharmony_ci return &*it; 1301cb0ef41Sopenharmony_ci } 1311cb0ef41Sopenharmony_ci 1321cb0ef41Sopenharmony_ci const Entry::Value* FindValue(const Entry* entry, int stack_index) const { 1331cb0ef41Sopenharmony_ci while (true) { 1341cb0ef41Sopenharmony_ci if (auto* value = entry->FindChangedValue(stack_index)) { 1351cb0ef41Sopenharmony_ci // Check that the table was correctly minimized: If the previous stack 1361cb0ef41Sopenharmony_ci // also had an entry for {stack_index}, it must be different. 1371cb0ef41Sopenharmony_ci DCHECK(entry == &entries_.front() || 1381cb0ef41Sopenharmony_ci (entry - 1)->stack_height() <= stack_index || 1391cb0ef41Sopenharmony_ci *FindValue(entry - 1, stack_index) != *value); 1401cb0ef41Sopenharmony_ci return value; 1411cb0ef41Sopenharmony_ci } 1421cb0ef41Sopenharmony_ci DCHECK_NE(&entries_.front(), entry); 1431cb0ef41Sopenharmony_ci --entry; 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci auto entries() const { 1481cb0ef41Sopenharmony_ci return base::make_iterator_range(entries_.begin(), entries_.end()); 1491cb0ef41Sopenharmony_ci } 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci int num_locals() const { return num_locals_; } 1521cb0ef41Sopenharmony_ci 1531cb0ef41Sopenharmony_ci void Print(std::ostream&) const; 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci private: 1561cb0ef41Sopenharmony_ci struct EntryPositionLess { 1571cb0ef41Sopenharmony_ci bool operator()(const Entry& a, const Entry& b) const { 1581cb0ef41Sopenharmony_ci return a.pc_offset() < b.pc_offset(); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci }; 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci int num_locals_; 1631cb0ef41Sopenharmony_ci std::vector<Entry> entries_; 1641cb0ef41Sopenharmony_ci}; 1651cb0ef41Sopenharmony_ci 1661cb0ef41Sopenharmony_ci// Debug info per NativeModule, created lazily on demand. 1671cb0ef41Sopenharmony_ci// Implementation in {wasm-debug.cc} using PIMPL. 1681cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE DebugInfo { 1691cb0ef41Sopenharmony_ci public: 1701cb0ef41Sopenharmony_ci explicit DebugInfo(NativeModule*); 1711cb0ef41Sopenharmony_ci ~DebugInfo(); 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ci // For the frame inspection methods below: 1741cb0ef41Sopenharmony_ci // {fp} is the frame pointer of the Liftoff frame, {debug_break_fp} that of 1751cb0ef41Sopenharmony_ci // the {WasmDebugBreak} frame (if any). 1761cb0ef41Sopenharmony_ci int GetNumLocals(Address pc); 1771cb0ef41Sopenharmony_ci WasmValue GetLocalValue(int local, Address pc, Address fp, 1781cb0ef41Sopenharmony_ci Address debug_break_fp, Isolate* isolate); 1791cb0ef41Sopenharmony_ci int GetStackDepth(Address pc); 1801cb0ef41Sopenharmony_ci 1811cb0ef41Sopenharmony_ci const wasm::WasmFunction& GetFunctionAtAddress(Address pc); 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci WasmValue GetStackValue(int index, Address pc, Address fp, 1841cb0ef41Sopenharmony_ci Address debug_break_fp, Isolate* isolate); 1851cb0ef41Sopenharmony_ci 1861cb0ef41Sopenharmony_ci // Returns the name of the entity (with the given |index| and |kind|) derived 1871cb0ef41Sopenharmony_ci // from the exports table. If the entity is not exported, an empty reference 1881cb0ef41Sopenharmony_ci // will be returned instead. 1891cb0ef41Sopenharmony_ci WireBytesRef GetExportName(ImportExportKindCode kind, uint32_t index); 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci // Returns the module and field name of the entity (with the given |index| 1921cb0ef41Sopenharmony_ci // and |kind|) derived from the imports table. If the entity is not imported, 1931cb0ef41Sopenharmony_ci // a pair of empty references will be returned instead. 1941cb0ef41Sopenharmony_ci std::pair<WireBytesRef, WireBytesRef> GetImportName(ImportExportKindCode kind, 1951cb0ef41Sopenharmony_ci uint32_t index); 1961cb0ef41Sopenharmony_ci 1971cb0ef41Sopenharmony_ci WireBytesRef GetTypeName(int type_index); 1981cb0ef41Sopenharmony_ci WireBytesRef GetLocalName(int func_index, int local_index); 1991cb0ef41Sopenharmony_ci WireBytesRef GetFieldName(int struct_index, int field_index); 2001cb0ef41Sopenharmony_ci 2011cb0ef41Sopenharmony_ci void SetBreakpoint(int func_index, int offset, Isolate* current_isolate); 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci // Returns true if we stay inside the passed frame (or a called frame) after 2041cb0ef41Sopenharmony_ci // the step. False if the frame will return after the step. 2051cb0ef41Sopenharmony_ci bool PrepareStep(WasmFrame*); 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci void PrepareStepOutTo(WasmFrame*); 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci void ClearStepping(Isolate*); 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci // Remove stepping code from a single frame; this is a performance 2121cb0ef41Sopenharmony_ci // optimization only, hitting debug breaks while not stepping and not at a set 2131cb0ef41Sopenharmony_ci // breakpoint would be unobservable otherwise. 2141cb0ef41Sopenharmony_ci void ClearStepping(WasmFrame*); 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ci bool IsStepping(WasmFrame*); 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci void RemoveBreakpoint(int func_index, int offset, Isolate* current_isolate); 2191cb0ef41Sopenharmony_ci 2201cb0ef41Sopenharmony_ci void RemoveDebugSideTables(base::Vector<WasmCode* const>); 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci // Return the debug side table for the given code object, but only if it has 2231cb0ef41Sopenharmony_ci // already been created. This will never trigger generation of the table. 2241cb0ef41Sopenharmony_ci DebugSideTable* GetDebugSideTableIfExists(const WasmCode*) const; 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci void RemoveIsolate(Isolate*); 2271cb0ef41Sopenharmony_ci 2281cb0ef41Sopenharmony_ci private: 2291cb0ef41Sopenharmony_ci std::unique_ptr<DebugInfoImpl> impl_; 2301cb0ef41Sopenharmony_ci}; 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci} // namespace wasm 2331cb0ef41Sopenharmony_ci} // namespace internal 2341cb0ef41Sopenharmony_ci} // namespace v8 2351cb0ef41Sopenharmony_ci 2361cb0ef41Sopenharmony_ci#endif // V8_WASM_WASM_DEBUG_H_ 237