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  }