github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/wazevoapi/perfmap.go (about)

     1  package wazevoapi
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strconv"
     7  	"sync"
     8  )
     9  
    10  var PerfMap *Perfmap
    11  
    12  func init() {
    13  	if PerfMapEnabled {
    14  		pid := os.Getpid()
    15  		filename := "/tmp/perf-" + strconv.Itoa(pid) + ".map"
    16  
    17  		fh, err := os.OpenFile(filename, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0o644)
    18  		if err != nil {
    19  			panic(err)
    20  		}
    21  
    22  		PerfMap = &Perfmap{fh: fh}
    23  	}
    24  }
    25  
    26  // Perfmap holds perfmap entries to be flushed into a perfmap file.
    27  type Perfmap struct {
    28  	entries []entry
    29  	mux     sync.Mutex
    30  	fh      *os.File
    31  }
    32  
    33  type entry struct {
    34  	index  int
    35  	offset int64
    36  	size   uint64
    37  	name   string
    38  }
    39  
    40  func (f *Perfmap) Lock() {
    41  	f.mux.Lock()
    42  }
    43  
    44  func (f *Perfmap) Unlock() {
    45  	f.mux.Unlock()
    46  }
    47  
    48  // AddModuleEntry adds a perfmap entry into the perfmap file.
    49  // index is the index of the function in the module, offset is the offset of the function in the module,
    50  // size is the size of the function, and name is the name of the function.
    51  //
    52  // Note that the entries are not flushed into the perfmap file until Flush is called,
    53  // and the entries are module-scoped; Perfmap must be locked until Flush is called.
    54  func (f *Perfmap) AddModuleEntry(index int, offset int64, size uint64, name string) {
    55  	e := entry{index: index, offset: offset, size: size, name: name}
    56  	if f.entries == nil {
    57  		f.entries = []entry{e}
    58  		return
    59  	}
    60  	f.entries = append(f.entries, e)
    61  }
    62  
    63  // Flush writes the perfmap entries into the perfmap file where the entries are adjusted by the given `addr` and `functionOffsets`.
    64  func (f *Perfmap) Flush(addr uintptr, functionOffsets []int) {
    65  	defer func() {
    66  		_ = f.fh.Sync()
    67  	}()
    68  
    69  	for _, e := range f.entries {
    70  		if _, err := f.fh.WriteString(fmt.Sprintf("%x %s %s\n",
    71  			uintptr(e.offset)+addr+uintptr(functionOffsets[e.index]),
    72  			strconv.FormatUint(e.size, 16),
    73  			e.name,
    74  		)); err != nil {
    75  			panic(err)
    76  		}
    77  	}
    78  	f.entries = f.entries[:0]
    79  }
    80  
    81  // Clear clears the perfmap entries not yet flushed.
    82  func (f *Perfmap) Clear() {
    83  	f.entries = f.entries[:0]
    84  }
    85  
    86  // AddEntry writes a perfmap entry directly into the perfmap file, not using the entries.
    87  func (f *Perfmap) AddEntry(addr uintptr, size uint64, name string) {
    88  	_, err := f.fh.WriteString(fmt.Sprintf("%x %s %s\n",
    89  		addr,
    90  		strconv.FormatUint(size, 16),
    91  		name,
    92  	))
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  }