github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/experimental/listener_example_test.go (about) 1 package experimental_test 2 3 import ( 4 "context" 5 _ "embed" 6 "fmt" 7 "log" 8 "sort" 9 10 "github.com/bananabytelabs/wazero" 11 "github.com/bananabytelabs/wazero/api" 12 "github.com/bananabytelabs/wazero/experimental" 13 "github.com/bananabytelabs/wazero/imports/wasi_snapshot_preview1" 14 "github.com/bananabytelabs/wazero/internal/wasm" 15 ) 16 17 // listenerWasm was generated by the following: 18 // 19 // cd testdata; wat2wasm --debug-names listener.wat 20 // 21 //go:embed logging/testdata/listener.wasm 22 var listenerWasm []byte 23 24 // uniqGoFuncs implements both FunctionListenerFactory and FunctionListener 25 type uniqGoFuncs map[string]struct{} 26 27 // callees returns the go functions called. 28 func (u uniqGoFuncs) callees() []string { 29 ret := make([]string, 0, len(u)) 30 for k := range u { 31 ret = append(ret, k) 32 } 33 // Sort names for consistent iteration 34 sort.Strings(ret) 35 return ret 36 } 37 38 // NewFunctionListener implements FunctionListenerFactory.NewFunctionListener 39 func (u uniqGoFuncs) NewFunctionListener(def api.FunctionDefinition) experimental.FunctionListener { 40 if def.GoFunction() == nil { 41 return nil // only track go funcs 42 } 43 return u 44 } 45 46 // Before implements FunctionListener.Before 47 func (u uniqGoFuncs) Before(ctx context.Context, _ api.Module, def api.FunctionDefinition, _ []uint64, _ experimental.StackIterator) { 48 u[def.DebugName()] = struct{}{} 49 } 50 51 // After implements FunctionListener.After 52 func (u uniqGoFuncs) After(context.Context, api.Module, api.FunctionDefinition, []uint64) {} 53 54 // Abort implements FunctionListener.Abort 55 func (u uniqGoFuncs) Abort(context.Context, api.Module, api.FunctionDefinition, error) {} 56 57 // This shows how to make a listener that counts go function calls. 58 func Example_customListenerFactory() { 59 u := uniqGoFuncs{} 60 61 // Set context to one that has an experimental listener 62 ctx := context.WithValue(context.Background(), experimental.FunctionListenerFactoryKey{}, u) 63 64 r := wazero.NewRuntime(ctx) 65 defer r.Close(ctx) // This closes everything this Runtime created. 66 67 wasi_snapshot_preview1.MustInstantiate(ctx, r) 68 69 mod, err := r.Instantiate(ctx, listenerWasm) 70 if err != nil { 71 log.Panicln(err) 72 } 73 74 for i := 0; i < 5; i++ { 75 if _, err = mod.ExportedFunction("rand").Call(ctx, 4); err != nil { 76 log.Panicln(err) 77 } 78 } 79 80 // A Go function was called multiple times, but we should only see it once. 81 for _, f := range u.callees() { 82 fmt.Println(f) 83 } 84 85 // Output: 86 // wasi_snapshot_preview1.fd_write 87 // wasi_snapshot_preview1.random_get 88 } 89 90 func Example_stackIterator() { 91 it := &fakeStackIterator{} 92 93 for it.Next() { 94 fn := it.Function() 95 pc := it.ProgramCounter() 96 fmt.Println("function:", fn.Definition().DebugName()) 97 fmt.Println("\tprogram counter:", pc) 98 fmt.Println("\tsource offset:", fn.SourceOffsetForPC(pc)) 99 } 100 101 // Output: 102 // function: fn0 103 // program counter: 5890831 104 // source offset: 1234 105 // function: fn1 106 // program counter: 5899822 107 // source offset: 7286 108 // function: fn2 109 // program counter: 6820312 110 // source offset: 935891 111 } 112 113 type fakeStackIterator struct { 114 iteration int 115 def api.FunctionDefinition 116 args []uint64 117 pc uint64 118 sourceOffset uint64 119 } 120 121 func (s *fakeStackIterator) Next() bool { 122 switch s.iteration { 123 case 0: 124 s.def = &mockFunctionDefinition{debugName: "fn0"} 125 s.args = []uint64{1, 2, 3} 126 s.pc = 5890831 127 s.sourceOffset = 1234 128 case 1: 129 s.def = &mockFunctionDefinition{debugName: "fn1"} 130 s.args = []uint64{} 131 s.pc = 5899822 132 s.sourceOffset = 7286 133 case 2: 134 s.def = &mockFunctionDefinition{debugName: "fn2"} 135 s.args = []uint64{4} 136 s.pc = 6820312 137 s.sourceOffset = 935891 138 case 3: 139 return false 140 } 141 s.iteration++ 142 return true 143 } 144 145 func (s *fakeStackIterator) Function() experimental.InternalFunction { 146 return internalFunction{ 147 definition: s.def, 148 sourceOffset: s.sourceOffset, 149 } 150 } 151 152 func (s *fakeStackIterator) ProgramCounter() experimental.ProgramCounter { 153 return experimental.ProgramCounter(s.pc) 154 } 155 156 var _ experimental.StackIterator = &fakeStackIterator{} 157 158 type internalFunction struct { 159 definition api.FunctionDefinition 160 sourceOffset uint64 161 } 162 163 func (f internalFunction) Definition() api.FunctionDefinition { 164 return f.definition 165 } 166 167 func (f internalFunction) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 { 168 return f.sourceOffset 169 } 170 171 type mockFunctionDefinition struct { 172 debugName string 173 *wasm.FunctionDefinition 174 } 175 176 func (f *mockFunctionDefinition) DebugName() string { 177 return f.debugName 178 } 179 180 func (f *mockFunctionDefinition) ParamTypes() []wasm.ValueType { 181 return []wasm.ValueType{} 182 } 183 184 func (f *mockFunctionDefinition) ResultTypes() []wasm.ValueType { 185 return []wasm.ValueType{} 186 }