github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/experimental/logging/log_listener_test.go (about)

     1  package logging_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"math"
     7  	"testing"
     8  
     9  	"github.com/bananabytelabs/wazero/api"
    10  	"github.com/bananabytelabs/wazero/experimental"
    11  	"github.com/bananabytelabs/wazero/experimental/logging"
    12  	"github.com/bananabytelabs/wazero/experimental/wazerotest"
    13  	"github.com/bananabytelabs/wazero/internal/testing/require"
    14  	wasi "github.com/bananabytelabs/wazero/internal/wasip1"
    15  	"github.com/bananabytelabs/wazero/internal/wasm"
    16  )
    17  
    18  // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors.
    19  var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
    20  
    21  func Test_loggingListener(t *testing.T) {
    22  	wasiFuncName := wasi.RandomGetName
    23  	wasiFuncType := wasm.FunctionType{
    24  		Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI32},
    25  		Results: []api.ValueType{api.ValueTypeI32},
    26  	}
    27  	wasiParamNames := []string{"buf", "buf_len"}
    28  	wasiParams := []uint64{0, 8}
    29  	wasiResultNames := []string{"errno"}
    30  
    31  	tests := []struct {
    32  		name                    string
    33  		moduleName, funcName    string
    34  		functype                wasm.FunctionType
    35  		isHostFunc              bool
    36  		paramNames, resultNames []string
    37  		params, results         []uint64
    38  		expected                string
    39  	}{
    40  		{
    41  			name:     "v_v",
    42  			functype: wasm.FunctionType{},
    43  			expected: `--> test.fn()
    44  <--
    45  `,
    46  		},
    47  		{
    48  			name:       "host",
    49  			functype:   wasm.FunctionType{},
    50  			isHostFunc: true,
    51  			expected: `==> test.fn()
    52  <==
    53  `,
    54  		},
    55  		{
    56  			name:        "wasi",
    57  			functype:    wasiFuncType,
    58  			moduleName:  wasi.InternalModuleName,
    59  			funcName:    wasiFuncName,
    60  			paramNames:  wasiParamNames,
    61  			resultNames: wasiResultNames,
    62  			isHostFunc:  true,
    63  			params:      wasiParams,
    64  			results:     []uint64{uint64(wasi.ErrnoSuccess)},
    65  			expected: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=8)
    66  <== errno=ESUCCESS
    67  `,
    68  		},
    69  		{
    70  			name:        "wasi errno",
    71  			functype:    wasiFuncType,
    72  			moduleName:  wasi.InternalModuleName,
    73  			funcName:    wasiFuncName,
    74  			paramNames:  wasiParamNames,
    75  			resultNames: wasiResultNames,
    76  			isHostFunc:  true,
    77  			params:      wasiParams,
    78  			results:     []uint64{uint64(wasi.ErrnoFault)},
    79  			expected: `==> wasi_snapshot_preview1.random_get(buf=0,buf_len=8)
    80  <== errno=EFAULT
    81  `,
    82  		},
    83  		{
    84  			name:     "i32",
    85  			functype: wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI32}},
    86  			params:   []uint64{math.MaxUint32},
    87  			expected: `--> test.fn(-1)
    88  <--
    89  `,
    90  		},
    91  		{
    92  			name:       "i32 named",
    93  			functype:   wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI32}},
    94  			params:     []uint64{math.MaxUint32},
    95  			paramNames: []string{"x"},
    96  			expected: `--> test.fn(x=-1)
    97  <--
    98  `,
    99  		},
   100  		{
   101  			name:     "i64",
   102  			functype: wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI64}},
   103  			params:   []uint64{math.MaxUint64},
   104  			expected: `--> test.fn(-1)
   105  <--
   106  `,
   107  		},
   108  		{
   109  			name:       "i64 named",
   110  			functype:   wasm.FunctionType{Params: []api.ValueType{api.ValueTypeI64}},
   111  			params:     []uint64{math.MaxUint64},
   112  			paramNames: []string{"x"},
   113  			expected: `--> test.fn(x=-1)
   114  <--
   115  `,
   116  		},
   117  		{
   118  			name:     "f32",
   119  			functype: wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF32}},
   120  			params:   []uint64{api.EncodeF32(math.MaxFloat32)},
   121  			expected: `--> test.fn(3.4028235e+38)
   122  <--
   123  `,
   124  		},
   125  		{
   126  			name:       "f32 named",
   127  			functype:   wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF32}},
   128  			params:     []uint64{api.EncodeF32(math.MaxFloat32)},
   129  			paramNames: []string{"x"},
   130  			expected: `--> test.fn(x=3.4028235e+38)
   131  <--
   132  `,
   133  		},
   134  		{
   135  			name:     "f64",
   136  			functype: wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF64}},
   137  			params:   []uint64{api.EncodeF64(math.MaxFloat64)},
   138  			expected: `--> test.fn(1.7976931348623157e+308)
   139  <--
   140  `,
   141  		},
   142  		{
   143  			name:       "f64 named",
   144  			functype:   wasm.FunctionType{Params: []api.ValueType{api.ValueTypeF64}},
   145  			params:     []uint64{api.EncodeF64(math.MaxFloat64)},
   146  			paramNames: []string{"x"},
   147  			expected: `--> test.fn(x=1.7976931348623157e+308)
   148  <--
   149  `,
   150  		},
   151  		{
   152  			name:     "externref",
   153  			functype: wasm.FunctionType{Params: []api.ValueType{api.ValueTypeExternref}},
   154  			params:   []uint64{0},
   155  			expected: `--> test.fn(0000000000000000)
   156  <--
   157  `,
   158  		},
   159  		{
   160  			name:       "externref named",
   161  			functype:   wasm.FunctionType{Params: []api.ValueType{api.ValueTypeExternref}},
   162  			params:     []uint64{0},
   163  			paramNames: []string{"x"},
   164  			expected: `--> test.fn(x=0000000000000000)
   165  <--
   166  `,
   167  		},
   168  		{
   169  			name:     "v128",
   170  			functype: wasm.FunctionType{Params: []api.ValueType{0x7b}},
   171  			params:   []uint64{0, 1},
   172  			expected: `--> test.fn(00000000000000000000000000000001)
   173  <--
   174  `,
   175  		},
   176  		{
   177  			name:       "v128 named",
   178  			functype:   wasm.FunctionType{Params: []api.ValueType{0x7b}},
   179  			params:     []uint64{0, 1},
   180  			paramNames: []string{"x"},
   181  			expected: `--> test.fn(x=00000000000000000000000000000001)
   182  <--
   183  `,
   184  		},
   185  		{
   186  			name:     "funcref",
   187  			functype: wasm.FunctionType{Params: []api.ValueType{0x70}},
   188  			params:   []uint64{0},
   189  			expected: `--> test.fn(0000000000000000)
   190  <--
   191  `,
   192  		},
   193  		{
   194  			name:       "funcref named",
   195  			functype:   wasm.FunctionType{Params: []api.ValueType{0x70}},
   196  			params:     []uint64{0},
   197  			paramNames: []string{"x"},
   198  			expected: `--> test.fn(x=0000000000000000)
   199  <--
   200  `,
   201  		},
   202  		{
   203  			name:     "no params, one result",
   204  			functype: wasm.FunctionType{Results: []api.ValueType{api.ValueTypeI32}},
   205  			results:  []uint64{math.MaxUint32},
   206  			expected: `--> test.fn()
   207  <-- -1
   208  `,
   209  		},
   210  		{
   211  			name: "one param, one result",
   212  			functype: wasm.FunctionType{
   213  				Params:  []api.ValueType{api.ValueTypeI32},
   214  				Results: []api.ValueType{api.ValueTypeF32},
   215  			},
   216  			params:  []uint64{math.MaxUint32},
   217  			results: []uint64{api.EncodeF32(math.MaxFloat32)},
   218  			expected: `--> test.fn(-1)
   219  <-- 3.4028235e+38
   220  `,
   221  		},
   222  		{
   223  			name: "two params, two results",
   224  			functype: wasm.FunctionType{
   225  				Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
   226  				Results: []api.ValueType{api.ValueTypeF32, api.ValueTypeF64},
   227  			},
   228  			params:  []uint64{math.MaxUint32, math.MaxUint64},
   229  			results: []uint64{api.EncodeF32(math.MaxFloat32), api.EncodeF64(math.MaxFloat64)},
   230  			expected: `--> test.fn(-1,-1)
   231  <-- (3.4028235e+38,1.7976931348623157e+308)
   232  `,
   233  		},
   234  		{
   235  			name: "two params, two results named",
   236  			functype: wasm.FunctionType{
   237  				Params:  []api.ValueType{api.ValueTypeI32, api.ValueTypeI64},
   238  				Results: []api.ValueType{api.ValueTypeF32, api.ValueTypeF64},
   239  			},
   240  			params:     []uint64{math.MaxUint32, math.MaxUint64},
   241  			paramNames: []string{"x", "y"},
   242  			results:    []uint64{api.EncodeF32(math.MaxFloat32), api.EncodeF64(math.MaxFloat64)},
   243  			expected: `--> test.fn(x=-1,y=-1)
   244  <-- (3.4028235e+38,1.7976931348623157e+308)
   245  `,
   246  		},
   247  	}
   248  
   249  	var out bytes.Buffer
   250  	lf := logging.NewLoggingListenerFactory(&out)
   251  	fn := func() {}
   252  	for _, tt := range tests {
   253  		tc := tt
   254  		t.Run(tc.name, func(t *testing.T) {
   255  			if tc.moduleName == "" {
   256  				tc.moduleName = "test"
   257  			}
   258  			if tc.funcName == "" {
   259  				tc.funcName = "fn"
   260  			}
   261  			m := &wasm.Module{
   262  				TypeSection:     []wasm.FunctionType{tc.functype},
   263  				FunctionSection: []wasm.Index{0},
   264  				NameSection: &wasm.NameSection{
   265  					ModuleName:    tc.moduleName,
   266  					FunctionNames: wasm.NameMap{{Name: tc.funcName}},
   267  					LocalNames:    wasm.IndirectNameMap{{NameMap: toNameMap(tc.paramNames)}},
   268  					ResultNames:   wasm.IndirectNameMap{{NameMap: toNameMap(tc.resultNames)}},
   269  				},
   270  			}
   271  
   272  			if tc.isHostFunc {
   273  				m.CodeSection = []wasm.Code{wasm.MustParseGoReflectFuncCode(fn)}
   274  			} else {
   275  				m.CodeSection = []wasm.Code{{Body: []byte{wasm.OpcodeEnd}}}
   276  			}
   277  			def := m.FunctionDefinition(0)
   278  			l := lf.NewFunctionListener(def)
   279  
   280  			out.Reset()
   281  			l.Before(testCtx, nil, def, tc.params, nil)
   282  			l.After(testCtx, nil, def, tc.results)
   283  			require.Equal(t, tc.expected, out.String())
   284  		})
   285  	}
   286  }
   287  
   288  func toNameMap(names []string) wasm.NameMap {
   289  	if len(names) == 0 {
   290  		return nil
   291  	}
   292  	var ret wasm.NameMap
   293  	for i, n := range names {
   294  		ret = append(ret, wasm.NameAssoc{Index: wasm.Index(i), Name: n})
   295  	}
   296  	return ret
   297  }
   298  
   299  func Test_loggingListener_indentation(t *testing.T) {
   300  	out := bytes.NewBuffer(nil)
   301  	lf := logging.NewLoggingListenerFactory(out)
   302  	m := &wasm.Module{
   303  		TypeSection:     []wasm.FunctionType{{}},
   304  		FunctionSection: []wasm.Index{0, 0},
   305  		CodeSection:     []wasm.Code{{Body: []byte{wasm.OpcodeEnd}}, {Body: []byte{wasm.OpcodeEnd}}},
   306  		NameSection: &wasm.NameSection{
   307  			ModuleName:    "test",
   308  			FunctionNames: wasm.NameMap{{Index: 0, Name: "fn1"}, {Index: 1, Name: "fn2"}},
   309  		},
   310  	}
   311  	def1 := m.FunctionDefinition(0)
   312  	l1 := lf.NewFunctionListener(def1)
   313  	def2 := m.FunctionDefinition(1)
   314  	l2 := lf.NewFunctionListener(def2)
   315  
   316  	l1.Before(testCtx, nil, def1, []uint64{}, nil)
   317  	l2.Before(testCtx, nil, def2, []uint64{}, nil)
   318  	l2.After(testCtx, nil, def2, []uint64{})
   319  	l1.After(testCtx, nil, def1, []uint64{})
   320  	require.Equal(t, `--> test.fn1()
   321  	--> test.fn2()
   322  	<--
   323  <--
   324  `, out.String())
   325  }
   326  
   327  func BenchmarkLoggingListener(b *testing.B) {
   328  	module := wazerotest.NewModule(
   329  		wazerotest.NewMemory(0),
   330  		wazerotest.NewFunction(func(ctx context.Context, mod api.Module) {}),
   331  	)
   332  
   333  	function := module.Function(0)
   334  	factory := logging.NewLoggingListenerFactory(discard{})
   335  	listener := factory.NewFunctionListener(function.Definition())
   336  
   337  	stack := []experimental.StackFrame{
   338  		{Function: function},
   339  	}
   340  
   341  	experimental.BenchmarkFunctionListener(b.N, module, stack, listener)
   342  }
   343  
   344  type discard struct{}
   345  
   346  func (discard) Write(b []byte) (int, error)       { return len(b), nil }
   347  func (discard) WriteString(s string) (int, error) { return len(s), nil }