This changes the listener signature to accept context and calling module, so that all possible parameters and results can be logged. This also changes the logging listener to make parameters visible when logging results. This infrastructure supports some helpful use cases, such as logging WASI result parameters, such as the prestat path, which is only knowable after the function has been called. The context parameter supposed reading results of gojs functions, which are stored host-side in a go context object. Future pull requests will complete this as well backfill unit tests. This is raised independently mainly to keep the PR size down of the upcoming filesystem logger. Signed-off-by: Adrian Cole <adrian@tetrate.io>
100 lines
3.7 KiB
Go
100 lines
3.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"github.com/tetratelabs/wazero"
|
|
"github.com/tetratelabs/wazero/api"
|
|
"github.com/tetratelabs/wazero/experimental"
|
|
"github.com/tetratelabs/wazero/experimental/logging"
|
|
"github.com/tetratelabs/wazero/internal/leb128"
|
|
"github.com/tetratelabs/wazero/internal/wasm"
|
|
binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary"
|
|
)
|
|
|
|
const proxyModuleName = "internal/testing/proxy/proxy.go"
|
|
|
|
// NewLoggingListenerFactory is like logging.NewHostLoggingListenerFactory,
|
|
// except it skips logging proxying functions from NewModuleBinary.
|
|
func NewLoggingListenerFactory(writer logging.Writer) experimental.FunctionListenerFactory {
|
|
return &loggingListenerFactory{logging.NewHostLoggingListenerFactory(writer)}
|
|
}
|
|
|
|
type loggingListenerFactory struct {
|
|
delegate experimental.FunctionListenerFactory
|
|
}
|
|
|
|
// NewListener implements the same method as documented on
|
|
// experimental.FunctionListener.
|
|
func (f *loggingListenerFactory) NewListener(fnd api.FunctionDefinition) experimental.FunctionListener {
|
|
if fnd.ModuleName() == proxyModuleName {
|
|
return nil // don't log proxy stuff
|
|
}
|
|
return f.delegate.NewListener(fnd)
|
|
}
|
|
|
|
// NewModuleBinary creates the proxy module to proxy a function call against
|
|
// all the exported functions in `proxyTarget`, and returns its encoded binary.
|
|
// The resulting module exports the proxy functions whose names are exactly the same
|
|
// as the proxy destination.
|
|
//
|
|
// This is used to test host call implementations. If logging, use
|
|
// NewLoggingListenerFactory to avoid messages from the proxying module.
|
|
func NewModuleBinary(moduleName string, proxyTarget wazero.CompiledModule) []byte {
|
|
funcDefs := proxyTarget.ExportedFunctions()
|
|
funcNum := uint32(len(funcDefs))
|
|
proxyModule := &wasm.Module{
|
|
MemorySection: &wasm.Memory{Min: 1},
|
|
ExportSection: []*wasm.Export{{Name: "memory", Type: api.ExternTypeMemory}},
|
|
NameSection: &wasm.NameSection{ModuleName: proxyModuleName},
|
|
}
|
|
var cnt wasm.Index
|
|
for _, def := range funcDefs {
|
|
proxyModule.TypeSection = append(proxyModule.TypeSection, &wasm.FunctionType{
|
|
Params: def.ParamTypes(), Results: def.ResultTypes(),
|
|
})
|
|
|
|
// Imports the function.
|
|
name := def.ExportNames()[0]
|
|
proxyModule.ImportSection = append(proxyModule.ImportSection, &wasm.Import{
|
|
Module: moduleName,
|
|
Name: name,
|
|
DescFunc: cnt,
|
|
})
|
|
|
|
// Ensures that type of the proxy function matches the imported function.
|
|
proxyModule.FunctionSection = append(proxyModule.FunctionSection, cnt)
|
|
|
|
// Build the function body of the proxy function.
|
|
var body []byte
|
|
for i := range def.ParamTypes() {
|
|
body = append(body, wasm.OpcodeLocalGet)
|
|
body = append(body, leb128.EncodeUint32(uint32(i))...)
|
|
}
|
|
|
|
body = append(body, wasm.OpcodeCall)
|
|
body = append(body, leb128.EncodeUint32(cnt)...)
|
|
body = append(body, wasm.OpcodeEnd)
|
|
proxyModule.CodeSection = append(proxyModule.CodeSection, &wasm.Code{Body: body})
|
|
|
|
proxyFuncIndex := cnt + funcNum
|
|
// Assigns the same params name as the imported one.
|
|
paramNames := &wasm.NameMapAssoc{Index: proxyFuncIndex}
|
|
for i, n := range def.ParamNames() {
|
|
paramNames.NameMap = append(paramNames.NameMap, &wasm.NameAssoc{Index: wasm.Index(i), Name: n})
|
|
}
|
|
proxyModule.NameSection.LocalNames = append(proxyModule.NameSection.LocalNames, paramNames)
|
|
|
|
// Plus, assigns the same function name.
|
|
proxyModule.NameSection.FunctionNames = append(proxyModule.NameSection.FunctionNames,
|
|
&wasm.NameAssoc{Index: proxyFuncIndex, Name: name})
|
|
|
|
// Finally, exports the proxy function with the same name as the imported one.
|
|
proxyModule.ExportSection = append(proxyModule.ExportSection, &wasm.Export{
|
|
Type: wasm.ExternTypeFunc,
|
|
Name: name,
|
|
Index: proxyFuncIndex,
|
|
})
|
|
cnt++
|
|
}
|
|
return binaryformat.EncodeModule(proxyModule)
|
|
}
|