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/wasm/wasm-debug.h" 6 7 #include "src/assert-scope.h" 8 #include "src/debug/debug.h" 9 #include "src/factory.h" 10 #include "src/isolate.h" 11 #include "src/wasm/module-decoder.h" 12 #include "src/wasm/wasm-module.h" 13 14 using namespace v8::internal; 15 using namespace v8::internal::wasm; 16 17 namespace { 18 19 enum { 20 kWasmDebugInfoWasmObj, 21 kWasmDebugInfoWasmBytesHash, 22 kWasmDebugInfoFunctionByteOffsets, 23 kWasmDebugInfoNumEntries 24 }; 25 26 ByteArray *GetOrCreateFunctionOffsetTable(Handle<WasmDebugInfo> debug_info) { 27 Object *offset_table = debug_info->get(kWasmDebugInfoFunctionByteOffsets); 28 Isolate *isolate = debug_info->GetIsolate(); 29 if (!offset_table->IsUndefined(isolate)) return ByteArray::cast(offset_table); 30 31 FunctionOffsetsResult function_offsets; 32 { 33 DisallowHeapAllocation no_gc; 34 SeqOneByteString *wasm_bytes = 35 wasm::GetWasmBytes(debug_info->wasm_object()); 36 const byte *bytes_start = wasm_bytes->GetChars(); 37 const byte *bytes_end = bytes_start + wasm_bytes->length(); 38 function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end); 39 } 40 DCHECK(function_offsets.ok()); 41 size_t array_size = 2 * kIntSize * function_offsets.val.size(); 42 CHECK_LE(array_size, static_cast<size_t>(kMaxInt)); 43 ByteArray *arr = 44 *isolate->factory()->NewByteArray(static_cast<int>(array_size)); 45 int idx = 0; 46 for (std::pair<int, int> p : function_offsets.val) { 47 arr->set_int(idx++, p.first); 48 arr->set_int(idx++, p.second); 49 } 50 DCHECK_EQ(arr->length(), idx * kIntSize); 51 debug_info->set(kWasmDebugInfoFunctionByteOffsets, arr); 52 53 return arr; 54 } 55 56 std::pair<int, int> GetFunctionOffsetAndLength(Handle<WasmDebugInfo> debug_info, 57 int func_index) { 58 ByteArray *arr = GetOrCreateFunctionOffsetTable(debug_info); 59 DCHECK(func_index >= 0 && func_index < arr->length() / kIntSize / 2); 60 61 int offset = arr->get_int(2 * func_index); 62 int length = arr->get_int(2 * func_index + 1); 63 // Assert that it's distinguishable from the "illegal function index" return. 64 DCHECK(offset > 0 && length > 0); 65 return {offset, length}; 66 } 67 68 Vector<const uint8_t> GetFunctionBytes(Handle<WasmDebugInfo> debug_info, 69 int func_index) { 70 SeqOneByteString *module_bytes = 71 wasm::GetWasmBytes(debug_info->wasm_object()); 72 std::pair<int, int> offset_and_length = 73 GetFunctionOffsetAndLength(debug_info, func_index); 74 return Vector<const uint8_t>( 75 module_bytes->GetChars() + offset_and_length.first, 76 offset_and_length.second); 77 } 78 79 } // namespace 80 81 Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) { 82 Isolate *isolate = wasm->GetIsolate(); 83 Factory *factory = isolate->factory(); 84 Handle<FixedArray> arr = 85 factory->NewFixedArray(kWasmDebugInfoNumEntries, TENURED); 86 arr->set(kWasmDebugInfoWasmObj, *wasm); 87 int hash = 0; 88 Handle<SeqOneByteString> wasm_bytes(GetWasmBytes(*wasm), isolate); 89 { 90 DisallowHeapAllocation no_gc; 91 hash = StringHasher::HashSequentialString( 92 wasm_bytes->GetChars(), wasm_bytes->length(), kZeroHashSeed); 93 } 94 Handle<Object> hash_obj = factory->NewNumberFromInt(hash, TENURED); 95 arr->set(kWasmDebugInfoWasmBytesHash, *hash_obj); 96 97 return Handle<WasmDebugInfo>::cast(arr); 98 } 99 100 bool WasmDebugInfo::IsDebugInfo(Object *object) { 101 if (!object->IsFixedArray()) return false; 102 FixedArray *arr = FixedArray::cast(object); 103 Isolate *isolate = arr->GetIsolate(); 104 return arr->length() == kWasmDebugInfoNumEntries && 105 IsWasmObject(arr->get(kWasmDebugInfoWasmObj)) && 106 arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber() && 107 (arr->get(kWasmDebugInfoFunctionByteOffsets)->IsUndefined(isolate) || 108 arr->get(kWasmDebugInfoFunctionByteOffsets)->IsByteArray()); 109 } 110 111 WasmDebugInfo *WasmDebugInfo::cast(Object *object) { 112 DCHECK(IsDebugInfo(object)); 113 return reinterpret_cast<WasmDebugInfo *>(object); 114 } 115 116 JSObject *WasmDebugInfo::wasm_object() { 117 return JSObject::cast(get(kWasmDebugInfoWasmObj)); 118 } 119 120 bool WasmDebugInfo::SetBreakPoint(int byte_offset) { 121 // TODO(clemensh): Implement this. 122 return false; 123 } 124 125 Handle<String> WasmDebugInfo::DisassembleFunction( 126 Handle<WasmDebugInfo> debug_info, int func_index) { 127 std::ostringstream disassembly_os; 128 129 { 130 Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index); 131 DisallowHeapAllocation no_gc; 132 133 base::AccountingAllocator allocator; 134 bool ok = PrintAst( 135 &allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()), 136 disassembly_os, nullptr); 137 DCHECK(ok); 138 USE(ok); 139 } 140 141 // Unfortunately, we have to copy the string here. 142 std::string code_str = disassembly_os.str(); 143 CHECK_LE(code_str.length(), static_cast<size_t>(kMaxInt)); 144 Factory *factory = debug_info->GetIsolate()->factory(); 145 Vector<const char> code_vec(code_str.data(), 146 static_cast<int>(code_str.length())); 147 return factory->NewStringFromAscii(code_vec).ToHandleChecked(); 148 } 149 150 Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable( 151 Handle<WasmDebugInfo> debug_info, int func_index) { 152 class NullBuf : public std::streambuf {}; 153 NullBuf null_buf; 154 std::ostream null_stream(&null_buf); 155 156 std::vector<std::tuple<uint32_t, int, int>> offset_table_vec; 157 158 { 159 Vector<const uint8_t> bytes_vec = GetFunctionBytes(debug_info, func_index); 160 DisallowHeapAllocation no_gc; 161 162 v8::base::AccountingAllocator allocator; 163 bool ok = PrintAst( 164 &allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()), 165 null_stream, &offset_table_vec); 166 DCHECK(ok); 167 USE(ok); 168 } 169 170 size_t arr_size = 3 * offset_table_vec.size(); 171 CHECK_LE(arr_size, static_cast<size_t>(kMaxInt)); 172 Factory *factory = debug_info->GetIsolate()->factory(); 173 Handle<FixedArray> offset_table = 174 factory->NewFixedArray(static_cast<int>(arr_size), TENURED); 175 176 int idx = 0; 177 for (std::tuple<uint32_t, int, int> elem : offset_table_vec) { 178 offset_table->set(idx++, Smi::FromInt(std::get<0>(elem))); 179 offset_table->set(idx++, Smi::FromInt(std::get<1>(elem))); 180 offset_table->set(idx++, Smi::FromInt(std::get<2>(elem))); 181 } 182 DCHECK_EQ(idx, offset_table->length()); 183 184 return offset_table; 185 } 186