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 }