logging: adds clock scope (#1071)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -123,10 +124,10 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer, exit func(cod
|
|||||||
"This may be specified multiple times. When <wasm path> is unset, <path> is used. "+
|
"This may be specified multiple times. When <wasm path> is unset, <path> is used. "+
|
||||||
"For read-only mounts, append the suffix ':ro'.")
|
"For read-only mounts, append the suffix ':ro'.")
|
||||||
|
|
||||||
var hostlogging sliceFlag
|
var hostlogging logScopesFlag
|
||||||
flags.Var(&hostlogging, "hostlogging",
|
flags.Var(&hostlogging, "hostlogging",
|
||||||
"A scope of host functions to log to stderr. "+
|
"A scope of host functions to log to stderr. "+
|
||||||
"This may be specified multiple times. Supported values: filesystem,random")
|
"This may be specified multiple times. Supported values: clock,filesystem,random")
|
||||||
|
|
||||||
cacheDir := cacheDirFlag(flags)
|
cacheDir := cacheDirFlag(flags)
|
||||||
|
|
||||||
@@ -173,7 +174,7 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer, exit func(cod
|
|||||||
|
|
||||||
wasmExe := filepath.Base(wasmPath)
|
wasmExe := filepath.Base(wasmPath)
|
||||||
|
|
||||||
ctx := maybeHostLogging(context.Background(), hostlogging, stdErr, exit)
|
ctx := maybeHostLogging(context.Background(), logging.LogScopes(hostlogging), stdErr)
|
||||||
|
|
||||||
rtc := wazero.NewRuntimeConfig()
|
rtc := wazero.NewRuntimeConfig()
|
||||||
if cache := maybeUseCacheDir(cacheDir, stdErr, exit); cache != nil {
|
if cache := maybeUseCacheDir(cacheDir, stdErr, exit); cache != nil {
|
||||||
@@ -294,25 +295,10 @@ func detectImports(imports []api.FunctionDefinition) (needsWASI, needsGo bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func maybeHostLogging(ctx context.Context, hostLogging []string, stdErr logging.Writer, exit func(code int)) context.Context {
|
func maybeHostLogging(ctx context.Context, scopes logging.LogScopes, stdErr logging.Writer) context.Context {
|
||||||
var scopes logging.LogScopes
|
|
||||||
for _, h := range hostLogging {
|
|
||||||
switch h {
|
|
||||||
case "":
|
|
||||||
case "random":
|
|
||||||
scopes |= logging.LogScopeRandom
|
|
||||||
case "filesystem":
|
|
||||||
scopes |= logging.LogScopeFilesystem
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(stdErr, "invalid hostLogging value: %v\n", h)
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if scopes != 0 {
|
if scopes != 0 {
|
||||||
ctx = context.WithValue(ctx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(stdErr, scopes))
|
return context.WithValue(ctx, experimental.FunctionListenerFactoryKey{}, logging.NewHostLoggingListenerFactory(stdErr, scopes))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,3 +360,24 @@ func (f *sliceFlag) Set(s string) error {
|
|||||||
*f = append(*f, s)
|
*f = append(*f, s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type logScopesFlag logging.LogScopes
|
||||||
|
|
||||||
|
func (f *logScopesFlag) String() string {
|
||||||
|
return logging.LogScopes(*f).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *logScopesFlag) Set(s string) error {
|
||||||
|
switch s {
|
||||||
|
case "":
|
||||||
|
case "clock":
|
||||||
|
*f |= logScopesFlag(logging.LogScopeClock)
|
||||||
|
case "filesystem":
|
||||||
|
*f |= logScopesFlag(logging.LogScopeFilesystem)
|
||||||
|
case "random":
|
||||||
|
*f |= logScopesFlag(logging.LogScopeRandom)
|
||||||
|
default:
|
||||||
|
return errors.New("not a log scope")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
"github.com/tetratelabs/wazero/experimental/logging"
|
||||||
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
|
"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
|
||||||
"github.com/tetratelabs/wazero/internal/platform"
|
"github.com/tetratelabs/wazero/internal/platform"
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
@@ -354,9 +355,9 @@ func TestRun(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cryptoTest := test{
|
cryptoTest := test{
|
||||||
name: "GOARCH=wasm GOOS=js hostlogging=random and filesystem",
|
name: "GOARCH=wasm GOOS=js hostlogging=filesystem random",
|
||||||
wasm: wasmCat,
|
wasm: wasmCat,
|
||||||
wazeroOpts: []string{"--hostlogging=random", "--hostlogging=filesystem"},
|
wazeroOpts: []string{"--hostlogging=filesystem", "--hostlogging=random"},
|
||||||
wasmArgs: []string{"/bear.txt"},
|
wasmArgs: []string{"/bear.txt"},
|
||||||
expectedStderr: `==> go.runtime.getRandomData(r_len=32)
|
expectedStderr: `==> go.runtime.getRandomData(r_len=32)
|
||||||
<==
|
<==
|
||||||
@@ -526,6 +527,55 @@ func Test_detectImports(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_logScopesFlag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
values []string
|
||||||
|
expected logging.LogScopes
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults to none",
|
||||||
|
expected: logging.LogScopeNone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignores empty",
|
||||||
|
values: []string{""},
|
||||||
|
expected: logging.LogScopeNone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clock",
|
||||||
|
values: []string{"clock"},
|
||||||
|
expected: logging.LogScopeClock,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filesystem",
|
||||||
|
values: []string{"filesystem"},
|
||||||
|
expected: logging.LogScopeFilesystem,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "random",
|
||||||
|
values: []string{"random"},
|
||||||
|
expected: logging.LogScopeRandom,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clock filesystem random",
|
||||||
|
values: []string{"clock", "filesystem", "random"},
|
||||||
|
expected: logging.LogScopeClock | logging.LogScopeFilesystem | logging.LogScopeRandom,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tc := tt
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
f := logScopesFlag(0)
|
||||||
|
for _, v := range tc.values {
|
||||||
|
require.NoError(t, f.Set(v))
|
||||||
|
}
|
||||||
|
require.Equal(t, tc.expected, logging.LogScopes(f))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHelp(t *testing.T) {
|
func TestHelp(t *testing.T) {
|
||||||
exitCode, _, stdErr := runMain(t, []string{"-h"})
|
exitCode, _, stdErr := runMain(t, []string{"-h"})
|
||||||
require.Equal(t, 0, exitCode)
|
require.Equal(t, 0, exitCode)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type LogScopes = logging.LogScopes
|
|||||||
const (
|
const (
|
||||||
// LogScopeNone means nothing should be logged
|
// LogScopeNone means nothing should be logged
|
||||||
LogScopeNone = logging.LogScopeNone
|
LogScopeNone = logging.LogScopeNone
|
||||||
|
// LogScopeClock enables logging for functions such as `clock_time_get`.
|
||||||
|
LogScopeClock = logging.LogScopeClock
|
||||||
// LogScopeFilesystem enables logging for functions such as `path_open`.
|
// LogScopeFilesystem enables logging for functions such as `path_open`.
|
||||||
// Note: This doesn't log writes to the console.
|
// Note: This doesn't log writes to the console.
|
||||||
LogScopeFilesystem = logging.LogScopeFilesystem
|
LogScopeFilesystem = logging.LogScopeFilesystem
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ func Test_clockResGet(t *testing.T) {
|
|||||||
clockID: ClockIDRealtime,
|
clockID: ClockIDRealtime,
|
||||||
expectedMemory: expectedMemoryMicro,
|
expectedMemory: expectedMemoryMicro,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_res_get(id=0,result.resolution=16)
|
==> wasi_snapshot_preview1.clock_res_get(id=realtime)
|
||||||
<== errno=ESUCCESS
|
<== (resolution=1000,errno=ESUCCESS)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -45,8 +45,8 @@ func Test_clockResGet(t *testing.T) {
|
|||||||
clockID: ClockIDMonotonic,
|
clockID: ClockIDMonotonic,
|
||||||
expectedMemory: expectedMemoryNano,
|
expectedMemory: expectedMemoryNano,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_res_get(id=1,result.resolution=16)
|
==> wasi_snapshot_preview1.clock_res_get(id=monotonic)
|
||||||
<== errno=ESUCCESS
|
<== (resolution=1,errno=ESUCCESS)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -85,8 +85,8 @@ func Test_clockResGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 2,
|
clockID: 2,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_res_get(id=2,result.resolution=16)
|
==> wasi_snapshot_preview1.clock_res_get(id=2)
|
||||||
<== errno=EINVAL
|
<== (resolution=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -94,8 +94,8 @@ func Test_clockResGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 3,
|
clockID: 3,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_res_get(id=3,result.resolution=16)
|
==> wasi_snapshot_preview1.clock_res_get(id=3)
|
||||||
<== errno=EINVAL
|
<== (resolution=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -103,12 +103,11 @@ func Test_clockResGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 100,
|
clockID: 100,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_res_get(id=100,result.resolution=16)
|
==> wasi_snapshot_preview1.clock_res_get(id=100)
|
||||||
<== errno=EINVAL
|
<== (resolution=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tc := tt
|
tc := tt
|
||||||
|
|
||||||
@@ -141,8 +140,8 @@ func Test_clockTimeGet(t *testing.T) {
|
|||||||
'?', // stopped after encoding
|
'?', // stopped after encoding
|
||||||
},
|
},
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=0,precision=0,result.timestamp=16)
|
==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
|
||||||
<== errno=ESUCCESS
|
<== (timestamp=1640995200000000000,errno=ESUCCESS)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -154,8 +153,8 @@ func Test_clockTimeGet(t *testing.T) {
|
|||||||
'?', // stopped after encoding
|
'?', // stopped after encoding
|
||||||
},
|
},
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=1,precision=0,result.timestamp=16)
|
==> wasi_snapshot_preview1.clock_time_get(id=monotonic,precision=0)
|
||||||
<== errno=ESUCCESS
|
<== (timestamp=0,errno=ESUCCESS)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -193,8 +192,8 @@ func Test_clockTimeGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 2,
|
clockID: 2,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=2,precision=0,result.timestamp=16)
|
==> wasi_snapshot_preview1.clock_time_get(id=2,precision=0)
|
||||||
<== errno=EINVAL
|
<== (timestamp=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -202,8 +201,8 @@ func Test_clockTimeGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 3,
|
clockID: 3,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=3,precision=0,result.timestamp=16)
|
==> wasi_snapshot_preview1.clock_time_get(id=3,precision=0)
|
||||||
<== errno=EINVAL
|
<== (timestamp=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -211,8 +210,8 @@ func Test_clockTimeGet_Unsupported(t *testing.T) {
|
|||||||
clockID: 100,
|
clockID: 100,
|
||||||
expectedErrno: ErrnoInval,
|
expectedErrno: ErrnoInval,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=100,precision=0,result.timestamp=16)
|
==> wasi_snapshot_preview1.clock_time_get(id=100,precision=0)
|
||||||
<== errno=EINVAL
|
<== (timestamp=,errno=EINVAL)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -245,16 +244,16 @@ func Test_clockTimeGet_Errors(t *testing.T) {
|
|||||||
name: "resultTimestamp OOM",
|
name: "resultTimestamp OOM",
|
||||||
resultTimestamp: memorySize,
|
resultTimestamp: memorySize,
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=0,precision=0,result.timestamp=65536)
|
==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
|
||||||
<== errno=EFAULT
|
<== (timestamp=,errno=EFAULT)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "resultTimestamp exceeds the maximum valid address by 1",
|
name: "resultTimestamp exceeds the maximum valid address by 1",
|
||||||
resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
|
resultTimestamp: memorySize - 4 + 1, // 4 is the size of uint32, the type of the count of args
|
||||||
expectedLog: `
|
expectedLog: `
|
||||||
==> wasi_snapshot_preview1.clock_time_get(id=0,precision=0,result.timestamp=65533)
|
==> wasi_snapshot_preview1.clock_time_get(id=realtime,precision=0)
|
||||||
<== errno=EFAULT
|
<== (timestamp=,errno=EFAULT)
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,10 @@ func Test_crypto(t *testing.T) {
|
|||||||
require.Equal(t, `7a0c9f9f0d
|
require.Equal(t, `7a0c9f9f0d
|
||||||
`, stdout)
|
`, stdout)
|
||||||
|
|
||||||
// TODO: Go 1.17 initializes randoms in a different order than Go 1.18,19
|
// Search for the two functions that should be in scope, flexibly, to pass
|
||||||
// When we move to 1.20, remove the workaround.
|
// go 1.17-19
|
||||||
if log.String() != `==> go.runtime.getRandomData(r_len=32)
|
require.Contains(t, log.String(), `go.runtime.getRandomData(r_len=32)
|
||||||
<==
|
<==`)
|
||||||
==> go.runtime.getRandomData(r_len=8)
|
require.Contains(t, log.String(), `==> go.syscall/js.valueCall(crypto.getRandomValues(r_len=5))
|
||||||
<==
|
<== (n=5)`)
|
||||||
==> go.syscall/js.valueCall(crypto.getRandomValues(r_len=5))
|
|
||||||
<== (n=5)
|
|
||||||
` {
|
|
||||||
require.Equal(t, `==> go.runtime.getRandomData(r_len=8)
|
|
||||||
<==
|
|
||||||
==> go.runtime.getRandomData(r_len=32)
|
|
||||||
<==
|
|
||||||
==> go.syscall/js.valueCall(crypto.getRandomValues(r_len=5))
|
|
||||||
<== (n=5)
|
|
||||||
`, log.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
17
internal/gojs/custom/date.go
Normal file
17
internal/gojs/custom/date.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package custom
|
||||||
|
|
||||||
|
const (
|
||||||
|
NameDate = "Date"
|
||||||
|
NameDateGetTimezoneOffset = "getTimezoneOffset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DateNameSection are the functions defined in the object named NameDate.
|
||||||
|
// Results here are those set to the current event object, but effectively are
|
||||||
|
// results of the host function.
|
||||||
|
var DateNameSection = map[string]*Names{
|
||||||
|
NameDateGetTimezoneOffset: {
|
||||||
|
Name: NameDateGetTimezoneOffset,
|
||||||
|
ParamNames: []string{},
|
||||||
|
ResultNames: []string{"tz"},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -190,5 +190,6 @@ var NameSection = map[string]*Names{
|
|||||||
|
|
||||||
var NameSectionSyscallValueCall = map[string]map[string]*Names{
|
var NameSectionSyscallValueCall = map[string]map[string]*Names{
|
||||||
NameCrypto: CryptoNameSection,
|
NameCrypto: CryptoNameSection,
|
||||||
|
NameDate: DateNameSection,
|
||||||
NameFs: FsNameSection,
|
NameFs: FsNameSection,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ import (
|
|||||||
|
|
||||||
// IsInLogScope returns true if the current function is in any of the scopes.
|
// IsInLogScope returns true if the current function is in any of the scopes.
|
||||||
func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
||||||
if scopes.IsEnabled(logging.LogScopeRandom) {
|
if scopes.IsEnabled(logging.LogScopeClock) {
|
||||||
switch fnd.Name() {
|
switch fnd.Name() {
|
||||||
case custom.NameRuntimeGetRandomData:
|
case custom.NameRuntimeNanotime1, custom.NameRuntimeWalltime:
|
||||||
return true
|
return true
|
||||||
case custom.NameSyscallValueCall: // e.g. crypto.getRandomValues
|
case custom.NameSyscallValueCall: // e.g. Date.getTimezoneOffset
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,15 @@ func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if scopes.IsEnabled(logging.LogScopeRandom) {
|
||||||
|
switch fnd.Name() {
|
||||||
|
case custom.NameRuntimeGetRandomData:
|
||||||
|
return true
|
||||||
|
case custom.NameSyscallValueCall: // e.g. crypto.getRandomValues
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return scopes == logging.LogScopeAll
|
return scopes == logging.LogScopeAll
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,14 +53,20 @@ func Config(fnd api.FunctionDefinition, scopes logging.LogScopes) (pSampler logg
|
|||||||
pLoggers = []logging.ParamLogger{syscallValueCallParamLogger}
|
pLoggers = []logging.ParamLogger{syscallValueCallParamLogger}
|
||||||
rLoggers = []logging.ResultLogger{syscallValueCallResultLogger}
|
rLoggers = []logging.ResultLogger{syscallValueCallResultLogger}
|
||||||
case custom.NameRuntimeGetRandomData:
|
case custom.NameRuntimeGetRandomData:
|
||||||
_, rLoggers = logging.Config(fnd)
|
pLoggers = []logging.ParamLogger{runtimeGetRandomDataParamLogger}
|
||||||
pLoggers = []logging.ParamLogger{syscallGetRandomParamLogger}
|
// no results
|
||||||
|
case custom.NameRuntimeNanotime1:
|
||||||
|
// no params
|
||||||
|
rLoggers = []logging.ResultLogger{runtimeNanotime1ResultLogger}
|
||||||
|
case custom.NameRuntimeWalltime:
|
||||||
|
// no params
|
||||||
|
rLoggers = []logging.ResultLogger{runtimeWalltimeResultLogger}
|
||||||
default: // TODO: make generic logger for gojs
|
default: // TODO: make generic logger for gojs
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func syscallGetRandomParamLogger(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
|
func runtimeGetRandomDataParamLogger(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
|
||||||
funcName := custom.NameRuntimeGetRandomData
|
funcName := custom.NameRuntimeGetRandomData
|
||||||
paramNames := custom.NameSection[funcName].ParamNames
|
paramNames := custom.NameSection[funcName].ParamNames
|
||||||
paramIdx := 1 /* there are two params, only write the length */
|
paramIdx := 1 /* there are two params, only write the length */
|
||||||
@@ -62,6 +77,28 @@ func syscallGetRandomParamLogger(_ context.Context, mod api.Module, w logging.Wr
|
|||||||
writeI32(w, stack.ParamUint32(paramIdx))
|
writeI32(w, stack.ParamUint32(paramIdx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runtimeNanotime1ResultLogger(_ context.Context, mod api.Module, w logging.Writer, params, _ []uint64) {
|
||||||
|
writeResults(w, custom.NameRuntimeNanotime1, mod, params, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runtimeWalltimeResultLogger(_ context.Context, mod api.Module, w logging.Writer, params, _ []uint64) {
|
||||||
|
writeResults(w, custom.NameRuntimeWalltime, mod, params, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeResults(w logging.Writer, funcName string, mod api.Module, params []uint64, resultOffset int) {
|
||||||
|
stack := goos.NewStack(funcName, mod.Memory(), uint32(params[0]))
|
||||||
|
|
||||||
|
resultNames := custom.NameSection[funcName].ResultNames
|
||||||
|
results := make([]interface{}, len(resultNames))
|
||||||
|
for i := range resultNames {
|
||||||
|
results[i] = stack.ParamUint32(i + resultOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteByte('(') //nolint
|
||||||
|
writeVals(w, resultNames, results)
|
||||||
|
w.WriteByte(')') //nolint
|
||||||
|
}
|
||||||
|
|
||||||
type syscallValueCallParamSampler struct {
|
type syscallValueCallParamSampler struct {
|
||||||
scopes logging.LogScopes
|
scopes logging.LogScopes
|
||||||
}
|
}
|
||||||
@@ -70,6 +107,10 @@ func (s *syscallValueCallParamSampler) isSampled(ctx context.Context, mod api.Mo
|
|||||||
vRef, m, args := syscallValueCallParams(ctx, mod, params)
|
vRef, m, args := syscallValueCallParams(ctx, mod, params)
|
||||||
|
|
||||||
switch vRef {
|
switch vRef {
|
||||||
|
case goos.RefJsCrypto:
|
||||||
|
return logging.LogScopeRandom.IsEnabled(s.scopes)
|
||||||
|
case goos.RefJsDate:
|
||||||
|
return logging.LogScopeClock.IsEnabled(s.scopes)
|
||||||
case goos.RefJsfs:
|
case goos.RefJsfs:
|
||||||
if !logging.LogScopeFilesystem.IsEnabled(s.scopes) {
|
if !logging.LogScopeFilesystem.IsEnabled(s.scopes) {
|
||||||
return false
|
return false
|
||||||
@@ -81,8 +122,6 @@ func (s *syscallValueCallParamSampler) isSampled(ctx context.Context, mod api.Mo
|
|||||||
return fd > sys.FdStderr
|
return fd > sys.FdStderr
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
case goos.RefJsCrypto:
|
|
||||||
return logging.LogScopeRandom.IsEnabled(s.scopes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.scopes == logging.LogScopeAll
|
return s.scopes == logging.LogScopeAll
|
||||||
@@ -94,6 +133,8 @@ func syscallValueCallParamLogger(ctx context.Context, mod api.Module, w logging.
|
|||||||
switch vRef {
|
switch vRef {
|
||||||
case goos.RefJsCrypto:
|
case goos.RefJsCrypto:
|
||||||
logSyscallValueCallArgs(w, custom.NameCrypto, m, args)
|
logSyscallValueCallArgs(w, custom.NameCrypto, m, args)
|
||||||
|
case goos.RefJsDate:
|
||||||
|
logSyscallValueCallArgs(w, custom.NameDate, m, args)
|
||||||
case goos.RefJsfs:
|
case goos.RefJsfs:
|
||||||
logFsParams(m, w, args)
|
logFsParams(m, w, args)
|
||||||
default:
|
default:
|
||||||
@@ -151,6 +192,10 @@ func syscallValueCallResultLogger(ctx context.Context, mod api.Module, w logging
|
|||||||
resultNames = custom.CryptoNameSection[m].ResultNames
|
resultNames = custom.CryptoNameSection[m].ResultNames
|
||||||
rRef := stack.ParamVal(ctx, 6, gojs.LoadValue) // val is after padding
|
rRef := stack.ParamVal(ctx, 6, gojs.LoadValue) // val is after padding
|
||||||
resultVals = []interface{}{rRef}
|
resultVals = []interface{}{rRef}
|
||||||
|
case goos.RefJsDate:
|
||||||
|
resultNames = custom.DateNameSection[m].ResultNames
|
||||||
|
rRef := stack.ParamVal(ctx, 6, gojs.LoadValue) // val is after padding
|
||||||
|
resultVals = []interface{}{rRef}
|
||||||
case goos.RefJsfs:
|
case goos.RefJsfs:
|
||||||
resultNames = custom.FsNameSection[m].ResultNames
|
resultNames = custom.FsNameSection[m].ResultNames
|
||||||
resultVals = gojs.GetLastEventArgs(ctx)
|
resultVals = gojs.GetLastEventArgs(ctx)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
"github.com/tetratelabs/wazero/internal/gojs/custom"
|
||||||
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
"github.com/tetratelabs/wazero/internal/gojs/goos"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,12 +12,12 @@ var (
|
|||||||
// jsDateConstructor returns jsDate.
|
// jsDateConstructor returns jsDate.
|
||||||
//
|
//
|
||||||
// This is defined as `Get("Date")` in zoneinfo_js.go time.initLocal
|
// This is defined as `Get("Date")` in zoneinfo_js.go time.initLocal
|
||||||
jsDateConstructor = newJsVal(goos.RefJsDateConstructor, "Date")
|
jsDateConstructor = newJsVal(goos.RefJsDateConstructor, custom.NameDate)
|
||||||
|
|
||||||
// jsDate is used inline in zoneinfo_js.go for time.initLocal.
|
// jsDate is used inline in zoneinfo_js.go for time.initLocal.
|
||||||
// `.Call("getTimezoneOffset").Int()` returns a timezone offset.
|
// `.Call("getTimezoneOffset").Int()` returns a timezone offset.
|
||||||
jsDate = newJsVal(goos.RefJsDate, "jsDate").
|
jsDate = newJsVal(goos.RefJsDate, custom.NameDate).
|
||||||
addFunction("getTimezoneOffset", jsDateGetTimezoneOffset{})
|
addFunction(custom.NameDateGetTimezoneOffset, jsDateGetTimezoneOffset{})
|
||||||
)
|
)
|
||||||
|
|
||||||
// jsDateGetTimezoneOffset implements jsFn
|
// jsDateGetTimezoneOffset implements jsFn
|
||||||
|
|||||||
@@ -1,20 +1,39 @@
|
|||||||
package gojs_test
|
package gojs_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tetratelabs/wazero"
|
"github.com/tetratelabs/wazero"
|
||||||
|
"github.com/tetratelabs/wazero/experimental"
|
||||||
|
"github.com/tetratelabs/wazero/experimental/logging"
|
||||||
"github.com/tetratelabs/wazero/internal/testing/require"
|
"github.com/tetratelabs/wazero/internal/testing/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_time(t *testing.T) {
|
func Test_time(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
stdout, stderr, err := compileAndRun(testCtx, "time", wazero.NewModuleConfig())
|
var log bytes.Buffer
|
||||||
|
loggingCtx := context.WithValue(testCtx, experimental.FunctionListenerFactoryKey{},
|
||||||
|
logging.NewHostLoggingListenerFactory(&log, logging.LogScopeClock))
|
||||||
|
|
||||||
|
stdout, stderr, err := compileAndRun(loggingCtx, "time", wazero.NewModuleConfig())
|
||||||
|
|
||||||
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
require.EqualError(t, err, `module "" closed with exit_code(0)`)
|
||||||
require.Zero(t, stderr)
|
require.Zero(t, stderr)
|
||||||
require.Equal(t, `Local
|
require.Equal(t, `Local
|
||||||
1ms
|
1ms
|
||||||
`, stdout)
|
`, stdout)
|
||||||
|
|
||||||
|
// Search for the three functions that should be in scope, flexibly, to pass
|
||||||
|
// go 1.17-19
|
||||||
|
require.Contains(t, log.String(), `==> go.runtime.nanotime1()
|
||||||
|
<== (nsec=0)`)
|
||||||
|
require.Contains(t, log.String(), `==> go.runtime.walltime()
|
||||||
|
<== (sec=1640995200,nsec=0)
|
||||||
|
`)
|
||||||
|
require.Contains(t, log.String(), `==> go.syscall/js.valueCall(Date.getTimezoneOffset())
|
||||||
|
<== (tz=0)
|
||||||
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,14 +37,17 @@ const (
|
|||||||
type LogScopes uint64
|
type LogScopes uint64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LogScopeNone = LogScopes(0)
|
LogScopeNone = LogScopes(0)
|
||||||
LogScopeFilesystem LogScopes = 1 << iota
|
LogScopeClock LogScopes = 1 << iota
|
||||||
|
LogScopeFilesystem
|
||||||
LogScopeRandom
|
LogScopeRandom
|
||||||
LogScopeAll = LogScopes(0xffffffffffffffff)
|
LogScopeAll = LogScopes(0xffffffffffffffff)
|
||||||
)
|
)
|
||||||
|
|
||||||
func scopeName(s LogScopes) string {
|
func scopeName(s LogScopes) string {
|
||||||
switch s {
|
switch s {
|
||||||
|
case LogScopeClock:
|
||||||
|
return "clock"
|
||||||
case LogScopeFilesystem:
|
case LogScopeFilesystem:
|
||||||
return "filesystem"
|
return "filesystem"
|
||||||
case LogScopeRandom:
|
case LogScopeRandom:
|
||||||
|
|||||||
@@ -51,10 +51,11 @@ func TestLogScopes_String(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{name: "none", scopes: LogScopeNone, expected: ""},
|
{name: "none", scopes: LogScopeNone, expected: ""},
|
||||||
{name: "any", scopes: LogScopeAll, expected: "all"},
|
{name: "any", scopes: LogScopeAll, expected: "all"},
|
||||||
|
{name: "clock", scopes: LogScopeClock, expected: "clock"},
|
||||||
{name: "filesystem", scopes: LogScopeFilesystem, expected: "filesystem"},
|
{name: "filesystem", scopes: LogScopeFilesystem, expected: "filesystem"},
|
||||||
{name: "random", scopes: LogScopeRandom, expected: "random"},
|
{name: "random", scopes: LogScopeRandom, expected: "random"},
|
||||||
{name: "filesystem|random", scopes: LogScopeFilesystem | LogScopeRandom, expected: "filesystem|random"},
|
{name: "filesystem|random", scopes: LogScopeFilesystem | LogScopeRandom, expected: "filesystem|random"},
|
||||||
{name: "undefined", scopes: 1 << 3, expected: fmt.Sprintf("<unknown=%d>", (1 << 3))},
|
{name: "undefined", scopes: 1 << 14, expected: fmt.Sprintf("<unknown=%d>", 1<<14)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ import (
|
|||||||
|
|
||||||
var le = binary.LittleEndian
|
var le = binary.LittleEndian
|
||||||
|
|
||||||
|
func isClockFunction(fnd api.FunctionDefinition) bool {
|
||||||
|
return strings.HasPrefix(fnd.Name(), "clock_")
|
||||||
|
}
|
||||||
|
|
||||||
func isFilesystemFunction(fnd api.FunctionDefinition) bool {
|
func isFilesystemFunction(fnd api.FunctionDefinition) bool {
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(fnd.Name(), "path_"):
|
case strings.HasPrefix(fnd.Name(), "path_"):
|
||||||
@@ -30,8 +34,8 @@ func isRandomFunction(fnd api.FunctionDefinition) bool {
|
|||||||
|
|
||||||
// IsInLogScope returns true if the current function is in any of the scopes.
|
// IsInLogScope returns true if the current function is in any of the scopes.
|
||||||
func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
||||||
if scopes.IsEnabled(logging.LogScopeRandom) {
|
if scopes.IsEnabled(logging.LogScopeClock) {
|
||||||
if isRandomFunction(fnd) {
|
if isClockFunction(fnd) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,12 @@ func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if scopes.IsEnabled(logging.LogScopeRandom) {
|
||||||
|
if isRandomFunction(fnd) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return scopes == logging.LogScopeAll
|
return scopes == logging.LogScopeAll
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +93,26 @@ func Config(fnd api.FunctionDefinition) (pSampler logging.ParamSampler, pLoggers
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(fnd.Name(), "clock_") {
|
||||||
|
switch name {
|
||||||
|
case "id":
|
||||||
|
logger = logClockId(idx).Log
|
||||||
|
pLoggers = append(pLoggers, logger)
|
||||||
|
case "result.resolution":
|
||||||
|
name = resultParamName(name)
|
||||||
|
logger = logMemI32(idx).Log
|
||||||
|
rLoggers = append(rLoggers, resultParamLogger(name, logger))
|
||||||
|
case "result.timestamp":
|
||||||
|
name = resultParamName(name)
|
||||||
|
logger = logMemI64(idx).Log
|
||||||
|
rLoggers = append(rLoggers, resultParamLogger(name, logger))
|
||||||
|
default:
|
||||||
|
logger = logging.NewParamLogger(idx, name, fnd.ParamTypes()[idx])
|
||||||
|
pLoggers = append(pLoggers, logger)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case "fdflags":
|
case "fdflags":
|
||||||
logger = logFdflags(idx).Log
|
logger = logFdflags(idx).Log
|
||||||
@@ -149,6 +179,14 @@ func (i logMemI32) Log(_ context.Context, mod api.Module, w logging.Writer, para
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type logMemI64 uint32
|
||||||
|
|
||||||
|
func (i logMemI64) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
|
||||||
|
if v, ok := mod.Memory().ReadUint64Le(uint32(params[i])); ok {
|
||||||
|
writeI64(w, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type logFilestat uint32
|
type logFilestat uint32
|
||||||
|
|
||||||
func (i logFilestat) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
|
func (i logFilestat) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
|
||||||
@@ -214,6 +252,21 @@ func resultParamLogger(name string, pLogger logging.ParamLogger) logging.ResultL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type logClockId int
|
||||||
|
|
||||||
|
func (i logClockId) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
|
||||||
|
id := uint32(params[i])
|
||||||
|
w.WriteString("id=") //nolint
|
||||||
|
switch id {
|
||||||
|
case ClockIDRealtime:
|
||||||
|
w.WriteString("realtime") //nolint
|
||||||
|
case ClockIDMonotonic:
|
||||||
|
w.WriteString("monotonic") //nolint
|
||||||
|
default:
|
||||||
|
writeI32(w, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type logFdflags int
|
type logFdflags int
|
||||||
|
|
||||||
func (i logFdflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
|
func (i logFdflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ func (f *testFunctionDefinition) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIsInLogScope(t *testing.T) {
|
func TestIsInLogScope(t *testing.T) {
|
||||||
randomGet := &testFunctionDefinition{name: RandomGetName}
|
clockTimeGet := &testFunctionDefinition{name: ClockTimeGetName}
|
||||||
fdRead := &testFunctionDefinition{name: FdReadName}
|
fdRead := &testFunctionDefinition{name: FdReadName}
|
||||||
|
randomGet := &testFunctionDefinition{name: RandomGetName}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fnd api.FunctionDefinition
|
fnd api.FunctionDefinition
|
||||||
@@ -30,32 +31,32 @@ func TestIsInLogScope(t *testing.T) {
|
|||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "randomGet in LogScopeRandom",
|
name: "clockTimeGet in LogScopeClock",
|
||||||
fnd: randomGet,
|
fnd: clockTimeGet,
|
||||||
scopes: logging.LogScopeRandom,
|
scopes: logging.LogScopeClock,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "randomGet not in LogScopeFilesystem",
|
name: "clockTimeGet not in LogScopeFilesystem",
|
||||||
fnd: randomGet,
|
fnd: clockTimeGet,
|
||||||
scopes: logging.LogScopeFilesystem,
|
scopes: logging.LogScopeFilesystem,
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "randomGet in LogScopeRandom|LogScopeFilesystem",
|
name: "clockTimeGet in LogScopeClock|LogScopeFilesystem",
|
||||||
fnd: randomGet,
|
fnd: clockTimeGet,
|
||||||
scopes: logging.LogScopeRandom | logging.LogScopeFilesystem,
|
scopes: logging.LogScopeClock | logging.LogScopeFilesystem,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "randomGet in LogScopeAll",
|
name: "clockTimeGet in LogScopeAll",
|
||||||
fnd: randomGet,
|
fnd: clockTimeGet,
|
||||||
scopes: logging.LogScopeAll,
|
scopes: logging.LogScopeAll,
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "randomGet not in LogScopeNone",
|
name: "clockTimeGet not in LogScopeNone",
|
||||||
fnd: randomGet,
|
fnd: clockTimeGet,
|
||||||
scopes: logging.LogScopeNone,
|
scopes: logging.LogScopeNone,
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
@@ -89,6 +90,36 @@ func TestIsInLogScope(t *testing.T) {
|
|||||||
scopes: logging.LogScopeNone,
|
scopes: logging.LogScopeNone,
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "randomGet in LogScopeRandom",
|
||||||
|
fnd: randomGet,
|
||||||
|
scopes: logging.LogScopeRandom,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "randomGet not in LogScopeFilesystem",
|
||||||
|
fnd: randomGet,
|
||||||
|
scopes: logging.LogScopeFilesystem,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "randomGet in LogScopeRandom|LogScopeFilesystem",
|
||||||
|
fnd: randomGet,
|
||||||
|
scopes: logging.LogScopeRandom | logging.LogScopeFilesystem,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "randomGet in LogScopeAll",
|
||||||
|
fnd: randomGet,
|
||||||
|
scopes: logging.LogScopeAll,
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "randomGet not in LogScopeNone",
|
||||||
|
fnd: randomGet,
|
||||||
|
scopes: logging.LogScopeNone,
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
Reference in New Issue
Block a user