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)
|
||||
n, err := reader.Read(header)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, false, fmt.Errorf("compilationcache: error reading header: %v", err)
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -148,30 +148,27 @@ func deserializeCodes(wazeroVersion string, reader io.Reader) (codes []*code, st
|
||||
codes = make([]*code, 0, functionsNum)
|
||||
|
||||
var eightBytes [8]byte
|
||||
var nativeCodeLen uint64
|
||||
for i := uint32(0); i < functionsNum; i++ {
|
||||
c := &code{}
|
||||
|
||||
// Read the stack pointer ceil.
|
||||
_, err = reader.Read(eightBytes[:])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("reading stack pointer ceil: %v", err)
|
||||
if c.stackPointerCeil, err = readUint64(reader, &eightBytes); err != nil {
|
||||
err = fmt.Errorf("compilationcache: error reading func[%d] stack pointer ceil: %v", i, err)
|
||||
break
|
||||
}
|
||||
|
||||
c.stackPointerCeil = binary.LittleEndian.Uint64(eightBytes[:])
|
||||
|
||||
// Read (and mmap) the native code.
|
||||
_, err = reader.Read(eightBytes[:])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("reading native code size: %v", err)
|
||||
if nativeCodeLen, err = readUint64(reader, &eightBytes); err != nil {
|
||||
err = fmt.Errorf("compilationcache: error reading func[%d] reading native code size: %v", i, err)
|
||||
break
|
||||
}
|
||||
|
||||
c.codeSegment, err = platform.MmapCodeSegment(reader, int(binary.LittleEndian.Uint64(eightBytes[:])))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("mmaping function: %v", err)
|
||||
if c.codeSegment, err = platform.MmapCodeSegment(reader, int(nativeCodeLen)); err != nil {
|
||||
err = fmt.Errorf("compilationcache: error mmapping func[%d] code (len=%d): %v", i, nativeCodeLen, err)
|
||||
break
|
||||
}
|
||||
|
||||
codes = append(codes, c)
|
||||
}
|
||||
|
||||
@@ -186,3 +183,24 @@ func deserializeCodes(wazeroVersion string, reader io.Reader) (codes []*code, st
|
||||
}
|
||||
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 (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"testing"
|
||||
"testing/iotest"
|
||||
|
||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||
"github.com/tetratelabs/wazero/internal/u32"
|
||||
@@ -79,7 +83,7 @@ func TestDeserializeCodes(t *testing.T) {
|
||||
|
||||
name: "invalid header",
|
||||
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.
|
||||
// Function index = 1.
|
||||
),
|
||||
expErr: "reading stack pointer ceil: EOF",
|
||||
expErr: "compilationcache: error reading func[1] stack pointer ceil: EOF",
|
||||
},
|
||||
{
|
||||
name: "reading native code size",
|
||||
@@ -172,7 +176,7 @@ func TestDeserializeCodes(t *testing.T) {
|
||||
// Function index = 1.
|
||||
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",
|
||||
@@ -190,7 +194,7 @@ func TestDeserializeCodes(t *testing.T) {
|
||||
u64.LeBytes(5), // length of code.
|
||||
// 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",
|
||||
ext: &testCache{caches: map[wasm.ModuleID][]byte{{}: {1, 2, 3}}},
|
||||
expErr: "invalid header length: 3",
|
||||
expErr: "compilationcache: invalid header length: 3",
|
||||
},
|
||||
{
|
||||
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
|
||||
type testCache struct {
|
||||
caches map[wasm.ModuleID][]byte
|
||||
|
||||
Reference in New Issue
Block a user