Files
wazero/internal/engine/wazevo/engine_cache_test.go
Takeshi Yoneda 35b1bfea31 wazevo: fixes memory leak (#1759)
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
2023-10-05 14:16:52 +09:00

256 lines
6.4 KiB
Go

package wazevo
import (
"bytes"
"crypto/sha256"
"io"
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/u32"
"github.com/tetratelabs/wazero/internal/u64"
"github.com/tetratelabs/wazero/internal/wasm"
)
var testVersion = "0.0.1"
func TestSerializeCompiledModule(t *testing.T) {
tests := []struct {
in *compiledModule
exp []byte
}{
{
in: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5}},
functionOffsets: []int{0},
},
exp: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(1), // number of functions.
u64.LeBytes(0), // offset.
u64.LeBytes(5), // length of code.
[]byte{1, 2, 3, 4, 5}, // code.
[]byte{0}, // no source map.
),
},
{
in: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5}},
functionOffsets: []int{0},
},
exp: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(1), // number of functions.
u64.LeBytes(0), // offset.
u64.LeBytes(5), // length of code.
[]byte{1, 2, 3, 4, 5}, // code.
[]byte{0}, // no source map.
),
},
{
in: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5, 1, 2, 3}},
functionOffsets: []int{0, 5},
},
exp: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(2), // number of functions.
// Function index = 0.
u64.LeBytes(0), // offset.
// Function index = 1.
u64.LeBytes(5), // offset.
// Executable.
u64.LeBytes(8), // length of code.
[]byte{1, 2, 3, 4, 5, 1, 2, 3}, // code.
[]byte{0}, // no source map.
),
},
}
for i, tc := range tests {
actual, err := io.ReadAll(serializeCompiledModule(testVersion, tc.in))
require.NoError(t, err, i)
require.Equal(t, tc.exp, actual, i)
}
}
func concat(ins ...[]byte) (ret []byte) {
for _, in := range ins {
ret = append(ret, in...)
}
return
}
func TestDeserializeCompiledModule(t *testing.T) {
tests := []struct {
name string
in []byte
importedFunctionCount uint32
expCompiledModule *compiledModule
expStaleCache bool
expErr string
}{
{
name: "invalid header",
in: []byte{1},
expErr: "compilationcache: invalid header length: 1",
},
{
name: "invalid magic",
in: concat(
[]byte{'a', 'b', 'c', 'd', 'e', 'f'},
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(1), // number of functions.
),
expErr: "compilationcache: invalid magic number: got WAZEVO but want abcdef",
},
{
name: "version mismatch",
in: concat(
magic,
[]byte{byte(len("1233123.1.1"))},
[]byte("1233123.1.1"),
u32.LeBytes(1), // number of functions.
),
expStaleCache: true,
},
{
name: "version mismatch",
in: concat(
magic,
[]byte{byte(len("0.0.0"))},
[]byte("0.0.0"),
u32.LeBytes(1), // number of functions.
),
expStaleCache: true,
},
{
name: "one function",
in: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(1), // number of functions.
u64.LeBytes(0), // offset.
// Executable.
u64.LeBytes(5), // size.
[]byte{1, 2, 3, 4, 5}, // machine code.
[]byte{0}, // no source map.
),
expCompiledModule: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5}},
functionOffsets: []int{0},
},
expStaleCache: false,
expErr: "",
},
{
name: "two functions",
in: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(2), // number of functions.
// Function index = 0.
u64.LeBytes(0), // offset.
// Function index = 1.
u64.LeBytes(7), // offset.
// Executable.
u64.LeBytes(10), // size.
[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // machine code.
[]byte{0}, // no source map.
),
importedFunctionCount: 1,
expCompiledModule: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
functionOffsets: []int{0, 7},
},
expStaleCache: false,
expErr: "",
},
{
name: "reading executable offset",
in: concat(
[]byte(magic),
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(2), // number of functions.
// Function index = 0.
u64.LeBytes(5), // offset.
// Function index = 1.
),
expErr: "compilationcache: error reading func[1] executable offset: EOF",
},
{
name: "mmapping",
in: concat(
[]byte(magic),
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(2), // number of functions.
// Function index = 0.
u64.LeBytes(0), // offset.
// Function index = 1.
u64.LeBytes(5), // offset.
// Executable.
u64.LeBytes(5), // size of the executable.
// Lack of machine code here.
),
expErr: "compilationcache: error reading executable (len=5): EOF",
},
{
name: "no source map presence",
in: concat(
magic,
[]byte{byte(len(testVersion))},
[]byte(testVersion),
u32.LeBytes(1), // number of functions.
u64.LeBytes(0), // offset.
// Executable.
u64.LeBytes(5), // size.
[]byte{1, 2, 3, 4, 5}, // machine code.
),
expCompiledModule: &compiledModule{
executables: &executables{executable: []byte{1, 2, 3, 4, 5}},
functionOffsets: []int{0},
},
expStaleCache: false,
expErr: "compilationcache: error reading source map presence: EOF",
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cm, staleCache, err := deserializeCompiledModule(testVersion, io.NopCloser(bytes.NewReader(tc.in)))
if tc.expErr != "" {
require.EqualError(t, err, tc.expErr)
} else {
require.NoError(t, err)
require.Equal(t, tc.expCompiledModule, cm)
}
require.Equal(t, tc.expStaleCache, staleCache)
})
}
}
func Test_fileCacheKey(t *testing.T) {
s := sha256.New()
s.Write([]byte("hello world"))
m := &wasm.Module{}
s.Sum(m.ID[:0])
original := m.ID
result := fileCacheKey(m)
require.Equal(t, original, m.ID)
require.NotEqual(t, original, result)
}