compilationcache: improves error messages (#799)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -125,11 +125,11 @@ func deserializeCodes(wazeroVersion string, reader io.Reader) (codes []*code, st
|
|||||||
header := make([]byte, cacheHeaderSize)
|
header := make([]byte, cacheHeaderSize)
|
||||||
n, err := reader.Read(header)
|
n, err := reader.Read(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, fmt.Errorf("compilationcache: error reading header: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != cacheHeaderSize {
|
if n != cacheHeaderSize {
|
||||||
return nil, false, fmt.Errorf("invalid header length: %d", n)
|
return nil, false, fmt.Errorf("compilationcache: invalid header length: %d", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the version compatibility.
|
// Check the version compatibility.
|
||||||
@@ -148,30 +148,27 @@ func deserializeCodes(wazeroVersion string, reader io.Reader) (codes []*code, st
|
|||||||
codes = make([]*code, 0, functionsNum)
|
codes = make([]*code, 0, functionsNum)
|
||||||
|
|
||||||
var eightBytes [8]byte
|
var eightBytes [8]byte
|
||||||
|
var nativeCodeLen uint64
|
||||||
for i := uint32(0); i < functionsNum; i++ {
|
for i := uint32(0); i < functionsNum; i++ {
|
||||||
c := &code{}
|
c := &code{}
|
||||||
|
|
||||||
// Read the stack pointer ceil.
|
// Read the stack pointer ceil.
|
||||||
_, err = reader.Read(eightBytes[:])
|
if c.stackPointerCeil, err = readUint64(reader, &eightBytes); err != nil {
|
||||||
if err != nil {
|
err = fmt.Errorf("compilationcache: error reading func[%d] stack pointer ceil: %v", i, err)
|
||||||
err = fmt.Errorf("reading stack pointer ceil: %v", err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
c.stackPointerCeil = binary.LittleEndian.Uint64(eightBytes[:])
|
|
||||||
|
|
||||||
// Read (and mmap) the native code.
|
// Read (and mmap) the native code.
|
||||||
_, err = reader.Read(eightBytes[:])
|
if nativeCodeLen, err = readUint64(reader, &eightBytes); err != nil {
|
||||||
if err != nil {
|
err = fmt.Errorf("compilationcache: error reading func[%d] reading native code size: %v", i, err)
|
||||||
err = fmt.Errorf("reading native code size: %v", err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
c.codeSegment, err = platform.MmapCodeSegment(reader, int(binary.LittleEndian.Uint64(eightBytes[:])))
|
if c.codeSegment, err = platform.MmapCodeSegment(reader, int(nativeCodeLen)); err != nil {
|
||||||
if err != nil {
|
err = fmt.Errorf("compilationcache: error mmapping func[%d] code (len=%d): %v", i, nativeCodeLen, err)
|
||||||
err = fmt.Errorf("mmaping function: %v", err)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
codes = append(codes, c)
|
codes = append(codes, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,3 +183,24 @@ func deserializeCodes(wazeroVersion string, reader io.Reader) (codes []*code, st
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readUint64 strictly reads a uint64 in little-endian byte order, using the
|
||||||
|
// given array as a buffer. This returns io.EOF if less than 8 bytes were read.
|
||||||
|
func readUint64(reader io.Reader, b *[8]byte) (uint64, error) {
|
||||||
|
s := b[0:8]
|
||||||
|
n, err := reader.Read(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if n < 8 { // more strict than reader.Read
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the u64 from the underlying buffer
|
||||||
|
ret := binary.LittleEndian.Uint64(s)
|
||||||
|
|
||||||
|
// clear the underlying array
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
b[i] = 0
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package compiler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/iotest"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
"github.com/tetratelabs/wazero/internal/u32"
|
"github.com/tetratelabs/wazero/internal/u32"
|
||||||
@@ -79,7 +83,7 @@ func TestDeserializeCodes(t *testing.T) {
|
|||||||
|
|
||||||
name: "invalid header",
|
name: "invalid header",
|
||||||
in: []byte{1},
|
in: []byte{1},
|
||||||
expErr: "invalid header length: 1",
|
expErr: "compilationcache: invalid header length: 1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -156,7 +160,7 @@ func TestDeserializeCodes(t *testing.T) {
|
|||||||
[]byte{1, 2, 3, 4, 5}, // code.
|
[]byte{1, 2, 3, 4, 5}, // code.
|
||||||
// Function index = 1.
|
// Function index = 1.
|
||||||
),
|
),
|
||||||
expErr: "reading stack pointer ceil: EOF",
|
expErr: "compilationcache: error reading func[1] stack pointer ceil: EOF",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reading native code size",
|
name: "reading native code size",
|
||||||
@@ -172,7 +176,7 @@ func TestDeserializeCodes(t *testing.T) {
|
|||||||
// Function index = 1.
|
// Function index = 1.
|
||||||
u64.LeBytes(12345), // stack pointer ceil.
|
u64.LeBytes(12345), // stack pointer ceil.
|
||||||
),
|
),
|
||||||
expErr: "reading native code size: EOF",
|
expErr: "compilationcache: error reading func[1] reading native code size: EOF",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "mmapping",
|
name: "mmapping",
|
||||||
@@ -190,7 +194,7 @@ func TestDeserializeCodes(t *testing.T) {
|
|||||||
u64.LeBytes(5), // length of code.
|
u64.LeBytes(5), // length of code.
|
||||||
// Lack of code here.
|
// Lack of code here.
|
||||||
),
|
),
|
||||||
expErr: "mmaping function: EOF",
|
expErr: "compilationcache: error mmapping func[1] code (len=5): EOF",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +237,7 @@ func TestEngine_getCodesFromCache(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "error in deserialization",
|
name: "error in deserialization",
|
||||||
ext: &testCache{caches: map[wasm.ModuleID][]byte{{}: {1, 2, 3}}},
|
ext: &testCache{caches: map[wasm.ModuleID][]byte{{}: {1, 2, 3}}},
|
||||||
expErr: "invalid header length: 3",
|
expErr: "compilationcache: invalid header length: 3",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "stale cache",
|
name: "stale cache",
|
||||||
@@ -329,6 +333,78 @@ func TestEngine_addCodesToCache(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_readUint64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
input: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "half",
|
||||||
|
input: math.MaxUint32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "max",
|
||||||
|
input: math.MaxUint64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tc := tt
|
||||||
|
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
input := make([]byte, 8)
|
||||||
|
binary.LittleEndian.PutUint64(input, tc.input)
|
||||||
|
|
||||||
|
var b [8]byte
|
||||||
|
n, err := readUint64(bytes.NewReader(input), &b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.input, n)
|
||||||
|
|
||||||
|
// ensure the buffer was cleared
|
||||||
|
var expectedB [8]byte
|
||||||
|
require.Equal(t, expectedB, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_readUint64_errors(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input io.Reader
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
input: bytes.NewReader([]byte{}),
|
||||||
|
expectedErr: "EOF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not enough",
|
||||||
|
input: bytes.NewReader([]byte{1, 2}),
|
||||||
|
expectedErr: "EOF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error reading",
|
||||||
|
input: iotest.ErrReader(errors.New("ice cream")),
|
||||||
|
expectedErr: "ice cream",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tc := tt
|
||||||
|
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var b [8]byte
|
||||||
|
_, err := readUint64(tc.input, &b)
|
||||||
|
require.EqualError(t, err, tc.expectedErr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testCache implements compilationcache.Cache
|
// testCache implements compilationcache.Cache
|
||||||
type testCache struct {
|
type testCache struct {
|
||||||
caches map[wasm.ModuleID][]byte
|
caches map[wasm.ModuleID][]byte
|
||||||
|
|||||||
Reference in New Issue
Block a user