Commit b8238f89 authored by clemensh's avatar clemensh Committed by Commit bot

[wasm] Split off debug info from wasm object

All debugging-related information is now stored inside a dedicated
object, which is only allocated if debugging support is needed.
This is also where later a reference to the interpreter will be stored
for executing to-be-debugged functions and providing stack inspection.

R=titzer@chromium.org, ahaas@chromium.org
BUG=chromium:613110

Review-Url: https://codereview.chromium.org/2050953003
Cr-Commit-Position: refs/heads/master@{#37055}
parent c7443874
......@@ -1478,6 +1478,8 @@ v8_source_set("v8_base") {
"src/wasm/module-decoder.h",
"src/wasm/switch-logic.cc",
"src/wasm/switch-logic.h",
"src/wasm/wasm-debug.cc",
"src/wasm/wasm-debug.h",
"src/wasm/wasm-external-refs.cc",
"src/wasm/wasm-external-refs.h",
"src/wasm/wasm-function-name-table.cc",
......
......@@ -619,9 +619,9 @@ RUNTIME_FUNCTION(Runtime_OrdinaryHasInstance) {
RUNTIME_FUNCTION(Runtime_IsWasmObject) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
bool is_wasm_object = object->IsJSObject() &&
wasm::IsWasmObject(Handle<JSObject>::cast(object));
CONVERT_ARG_CHECKED(Object, object, 0);
bool is_wasm_object =
object->IsJSObject() && wasm::IsWasmObject(JSObject::cast(object));
return *isolate->factory()->ToBoolean(is_wasm_object);
}
......
......@@ -1162,6 +1162,8 @@
'wasm/module-decoder.h',
'wasm/switch-logic.h',
'wasm/switch-logic.cc',
'wasm/wasm-debug.cc',
'wasm/wasm-debug.h',
'wasm/wasm-external-refs.cc',
'wasm/wasm-external-refs.h',
'wasm/wasm-function-name-table.cc',
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/wasm/wasm-debug.h"
#include "src/assert-scope.h"
#include "src/debug/debug.h"
#include "src/factory.h"
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module.h"
using namespace v8::internal;
using namespace v8::internal::wasm;
namespace {
enum {
kWasmDebugInfoWasmObj,
kWasmDebugInfoWasmBytesHash,
kWasmDebugInfoFunctionByteOffsets,
kWasmDebugInfoNumEntries
};
ByteArray *GetOrCreateFunctionOffsetTable(WasmDebugInfo *debug_info) {
Object *offset_table = debug_info->get(kWasmDebugInfoFunctionByteOffsets);
Isolate *isolate = debug_info->GetIsolate();
if (!offset_table->IsUndefined(isolate)) return ByteArray::cast(offset_table);
FunctionOffsetsResult function_offsets;
{
DisallowHeapAllocation no_gc;
SeqOneByteString *wasm_bytes =
wasm::GetWasmBytes(debug_info->wasm_object());
const byte *bytes_start = wasm_bytes->GetChars();
const byte *bytes_end = bytes_start + wasm_bytes->length();
function_offsets = wasm::DecodeWasmFunctionOffsets(bytes_start, bytes_end);
}
DCHECK(function_offsets.ok());
size_t array_size = 2 * kIntSize * function_offsets.val.size();
CHECK_LE(array_size, static_cast<size_t>(kMaxInt));
ByteArray *arr =
*isolate->factory()->NewByteArray(static_cast<int>(array_size));
int idx = 0;
for (std::pair<int, int> p : function_offsets.val) {
arr->set_int(idx++, p.first);
arr->set_int(idx++, p.second);
}
DCHECK_EQ(arr->length(), idx * kIntSize);
debug_info->set(kWasmDebugInfoFunctionByteOffsets, arr);
return arr;
}
std::pair<int, int> GetFunctionOffsetAndLength(WasmDebugInfo *debug_info,
int func_index) {
ByteArray *arr = GetOrCreateFunctionOffsetTable(debug_info);
DCHECK(func_index >= 0 && func_index < arr->length() / kIntSize / 2);
int offset = arr->get_int(2 * func_index);
int length = arr->get_int(2 * func_index + 1);
// Assert that it's distinguishable from the "illegal function index" return.
DCHECK(offset > 0 && length > 0);
return {offset, length};
}
Vector<const uint8_t> GetFunctionBytes(WasmDebugInfo *debug_info,
int func_index) {
DCHECK(!AllowHeapAllocation::IsAllowed());
SeqOneByteString *module_bytes =
wasm::GetWasmBytes(debug_info->wasm_object());
std::pair<int, int> offset_and_length =
GetFunctionOffsetAndLength(debug_info, func_index);
return Vector<const uint8_t>(
module_bytes->GetChars() + offset_and_length.first,
offset_and_length.second);
}
} // namespace
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<JSObject> wasm) {
Isolate *isolate = wasm->GetIsolate();
Factory *factory = isolate->factory();
Handle<FixedArray> arr =
factory->NewFixedArray(kWasmDebugInfoNumEntries, TENURED);
arr->set(kWasmDebugInfoWasmObj, *wasm);
int hash = 0;
Handle<SeqOneByteString> wasm_bytes(GetWasmBytes(*wasm), isolate);
{
DisallowHeapAllocation no_gc;
hash = StringHasher::HashSequentialString(
wasm_bytes->GetChars(), wasm_bytes->length(), kZeroHashSeed);
}
Handle<Object> hash_obj = factory->NewNumberFromInt(hash, TENURED);
arr->set(kWasmDebugInfoWasmBytesHash, *hash_obj);
return Handle<WasmDebugInfo>::cast(arr);
}
bool WasmDebugInfo::IsDebugInfo(Object *object) {
if (!object->IsFixedArray()) return false;
FixedArray *arr = FixedArray::cast(object);
Isolate *isolate = arr->GetIsolate();
return arr->length() == kWasmDebugInfoNumEntries &&
IsWasmObject(arr->get(kWasmDebugInfoWasmObj)) &&
arr->get(kWasmDebugInfoWasmBytesHash)->IsNumber() &&
(arr->get(kWasmDebugInfoFunctionByteOffsets)->IsUndefined(isolate) ||
arr->get(kWasmDebugInfoFunctionByteOffsets)->IsByteArray());
}
WasmDebugInfo *WasmDebugInfo::cast(Object *object) {
DCHECK(IsDebugInfo(object));
return reinterpret_cast<WasmDebugInfo *>(object);
}
JSObject *WasmDebugInfo::wasm_object() {
return JSObject::cast(get(kWasmDebugInfoWasmObj));
}
bool WasmDebugInfo::SetBreakPoint(int byte_offset) {
// TODO(clemensh): Implement this.
return false;
}
Handle<String> WasmDebugInfo::DisassembleFunction(int func_index) {
std::ostringstream disassembly_os;
{
DisallowHeapAllocation no_gc;
Vector<const uint8_t> bytes_vec = GetFunctionBytes(this, func_index);
base::AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
disassembly_os, nullptr);
DCHECK(ok);
USE(ok);
}
// Unfortunately, we have to copy the string here.
std::string code_str = disassembly_os.str();
CHECK_LE(code_str.length(), static_cast<size_t>(kMaxInt));
return GetIsolate()
->factory()
->NewStringFromAscii(Vector<const char>(
code_str.data(), static_cast<int>(code_str.length())))
.ToHandleChecked();
}
Handle<FixedArray> WasmDebugInfo::GetFunctionOffsetTable(int func_index) {
class NullBuf : public std::streambuf {};
NullBuf null_buf;
std::ostream null_stream(&null_buf);
std::vector<std::tuple<uint32_t, int, int>> offset_table_vec;
{
DisallowHeapAllocation no_gc;
Vector<const uint8_t> bytes_vec = GetFunctionBytes(this, func_index);
v8::base::AccountingAllocator allocator;
bool ok = PrintAst(
&allocator, FunctionBodyForTesting(bytes_vec.start(), bytes_vec.end()),
null_stream, &offset_table_vec);
DCHECK(ok);
USE(ok);
}
size_t arr_size = 3 * offset_table_vec.size();
CHECK_LE(arr_size, static_cast<size_t>(kMaxInt));
Handle<FixedArray> offset_table = GetIsolate()->factory()->NewFixedArray(
static_cast<int>(arr_size), TENURED);
int idx = 0;
for (std::tuple<uint32_t, int, int> elem : offset_table_vec) {
offset_table->set(idx++, Smi::FromInt(std::get<0>(elem)));
offset_table->set(idx++, Smi::FromInt(std::get<1>(elem)));
offset_table->set(idx++, Smi::FromInt(std::get<2>(elem)));
}
DCHECK_EQ(idx, offset_table->length());
return offset_table;
}
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_WASM_DEBUG_H_
#define V8_WASM_DEBUG_H_
#include "src/handles.h"
#include "src/objects.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmDebugInfo : public FixedArray {
public:
static Handle<WasmDebugInfo> New(Handle<JSObject> wasm);
static bool IsDebugInfo(Object* object);
static WasmDebugInfo* cast(Object* object);
JSObject* wasm_object();
bool SetBreakPoint(int byte_offset);
// Disassemble the specified function from this module.
Handle<String> DisassembleFunction(int func_index);
// Get the offset table for the specified function.
// Returns an array with three entries per instruction: byte offset, line and
// column.
Handle<FixedArray> GetFunctionOffsetTable(int func_index);
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_DEBUG_H_
......@@ -12,6 +12,7 @@
#include "src/wasm/ast-decoder.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-function-name-table.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-result.h"
......@@ -205,12 +206,15 @@ class WasmLinker {
namespace {
// Internal constants for the layout of the module object.
const int kWasmModuleInternalFieldCount = 5;
const int kWasmModuleFunctionTable = 0;
const int kWasmModuleCodeTable = 1;
const int kWasmMemArrayBuffer = 2;
const int kWasmGlobalsArrayBuffer = 3;
// TODO(clemensh): Remove function name array, extract names from module bytes.
const int kWasmFunctionNamesArray = 4;
const int kWasmModuleBytesString = 5;
const int kWasmDebugInfo = 6;
const int kWasmModuleInternalFieldCount = 7;
void LoadDataSegments(const WasmModule* module, byte* mem_addr,
size_t mem_size) {
......@@ -701,6 +705,16 @@ MaybeHandle<JSObject> WasmModule::Instantiate(
Handle<FixedArray> code_table =
factory->NewFixedArray(static_cast<int>(functions.size()), TENURED);
instance.js_object->SetInternalField(kWasmModuleCodeTable, *code_table);
size_t module_bytes_len =
instance.module->module_end - instance.module->module_start;
DCHECK_LE(module_bytes_len, static_cast<size_t>(kMaxInt));
Vector<const uint8_t> module_bytes_vec(instance.module->module_start,
static_cast<int>(module_bytes_len));
Handle<String> module_bytes_string =
factory->NewStringFromOneByte(module_bytes_vec, TENURED)
.ToHandleChecked();
instance.js_object->SetInternalField(kWasmModuleBytesString,
*module_bytes_string);
//-------------------------------------------------------------------------
// Allocate and initialize the linear memory.
......@@ -1010,12 +1024,36 @@ Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm,
return isolate->factory()->NewStringFromStaticChars("<WASM UNNAMED>");
}
bool IsWasmObject(Handle<JSObject> object) {
// TODO(clemensh): Check wasm byte header once we store a copy of the bytes.
return object->GetInternalFieldCount() == kWasmModuleInternalFieldCount &&
object->GetInternalField(kWasmModuleCodeTable)->IsFixedArray() &&
object->GetInternalField(kWasmMemArrayBuffer)->IsJSArrayBuffer() &&
object->GetInternalField(kWasmFunctionNamesArray)->IsByteArray();
bool IsWasmObject(Object* object) {
if (!object->IsJSObject()) return false;
JSObject* obj = JSObject::cast(object);
if (obj->GetInternalFieldCount() != kWasmModuleInternalFieldCount ||
!obj->GetInternalField(kWasmModuleCodeTable)->IsFixedArray() ||
!obj->GetInternalField(kWasmMemArrayBuffer)->IsJSArrayBuffer() ||
!obj->GetInternalField(kWasmFunctionNamesArray)->IsByteArray() ||
!obj->GetInternalField(kWasmModuleBytesString)->IsSeqOneByteString()) {
return false;
}
DisallowHeapAllocation no_gc;
SeqOneByteString* bytes =
SeqOneByteString::cast(obj->GetInternalField(kWasmModuleBytesString));
if (bytes->length() < 4) return false;
if (memcmp(bytes->GetChars(), "\0asm", 4)) return false;
// All checks passed.
return true;
}
SeqOneByteString* GetWasmBytes(JSObject* wasm) {
return SeqOneByteString::cast(wasm->GetInternalField(kWasmModuleBytesString));
}
WasmDebugInfo* GetDebugInfo(JSObject* wasm) {
Object* info = wasm->GetInternalField(kWasmDebugInfo);
if (!info->IsUndefined(wasm->GetIsolate())) return WasmDebugInfo::cast(info);
Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(handle(wasm));
wasm->SetInternalField(kWasmDebugInfo, *new_info);
return *new_info;
}
} // namespace wasm
......
......@@ -73,6 +73,8 @@ const uint8_t kWasmFunctionTypeForm = 0x40;
#define WASM_SECTION_FUNCTION_BODIES_SIZE ((size_t)5)
#define WASM_SECTION_NAMES_SIZE ((size_t)5)
class WasmDebugInfo;
struct WasmSection {
enum class Code : uint32_t {
#define F(enumerator, order, string) enumerator,
......@@ -331,12 +333,19 @@ Handle<String> GetWasmFunctionName(Isolate* isolate, Handle<Object> wasm,
Handle<Object> GetWasmFunctionNameOrNull(Isolate* isolate, Handle<Object> wasm,
uint32_t func_index);
// Return the binary source bytes of a wasm module.
SeqOneByteString* GetWasmBytes(JSObject* wasm);
// Get the debug info associated with the given wasm object.
// If no debug info exists yet, it is created automatically.
WasmDebugInfo* GetDebugInfo(JSObject* wasm);
// Check whether the given object is a wasm object.
// This checks the number and type of internal fields, so it's not 100 percent
// secure. If it turns out that we need more complete checks, we could add a
// special marker as internal field, which will definitely never occur anywhere
// else.
bool IsWasmObject(Handle<JSObject> object);
bool IsWasmObject(Object* object);
} // namespace wasm
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment