github.com/tetratelabs/wazero@v1.2.1/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/tetratelabs/wazero" 11 "github.com/tetratelabs/wazero/api" 12 "github.com/tetratelabs/wazero/experimental" 13 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 14 "github.com/tetratelabs/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("\tparameters:", it.Parameters()) 98 fmt.Println("\tprogram counter:", pc) 99 fmt.Println("\tsource offset:", fn.SourceOffsetForPC(pc)) 100 } 101 102 // Output: 103 // function: fn0 104 // parameters: [1 2 3] 105 // program counter: 5890831 106 // source offset: 1234 107 // function: fn1 108 // parameters: [] 109 // program counter: 5899822 110 // source offset: 7286 111 // function: fn2 112 // parameters: [4] 113 // program counter: 6820312 114 // source offset: 935891 115 } 116 117 type fakeStackIterator struct { 118 iteration int 119 def api.FunctionDefinition 120 args []uint64 121 pc uint64 122 sourceOffset uint64 123 } 124 125 func (s *fakeStackIterator) Next() bool { 126 switch s.iteration { 127 case 0: 128 s.def = &mockFunctionDefinition{debugName: "fn0"} 129 s.args = []uint64{1, 2, 3} 130 s.pc = 5890831 131 s.sourceOffset = 1234 132 case 1: 133 s.def = &mockFunctionDefinition{debugName: "fn1"} 134 s.args = []uint64{} 135 s.pc = 5899822 136 s.sourceOffset = 7286 137 case 2: 138 s.def = &mockFunctionDefinition{debugName: "fn2"} 139 s.args = []uint64{4} 140 s.pc = 6820312 141 s.sourceOffset = 935891 142 case 3: 143 return false 144 } 145 s.iteration++ 146 return true 147 } 148 149 func (s *fakeStackIterator) Function() experimental.InternalFunction { 150 return internalFunction{ 151 definition: s.def, 152 sourceOffset: s.sourceOffset, 153 } 154 } 155 156 func (s *fakeStackIterator) Parameters() []uint64 { 157 return s.args 158 } 159 160 func (s *fakeStackIterator) ProgramCounter() experimental.ProgramCounter { 161 return experimental.ProgramCounter(s.pc) 162 } 163 164 var _ experimental.StackIterator = &fakeStackIterator{} 165 166 type internalFunction struct { 167 definition api.FunctionDefinition 168 sourceOffset uint64 169 } 170 171 func (f internalFunction) Definition() api.FunctionDefinition { 172 return f.definition 173 } 174 175 func (f internalFunction) SourceOffsetForPC(pc experimental.ProgramCounter) uint64 { 176 return f.sourceOffset 177 } 178 179 type mockFunctionDefinition struct { 180 debugName string 181 *wasm.FunctionDefinition 182 } 183 184 func (f *mockFunctionDefinition) DebugName() string { 185 return f.debugName 186 } 187 188 func (f *mockFunctionDefinition) ParamTypes() []wasm.ValueType { 189 return []wasm.ValueType{} 190 } 191 192 func (f *mockFunctionDefinition) ResultTypes() []wasm.ValueType { 193 return []wasm.ValueType{} 194 }