github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasip1/logging/logging.go (about)

     1  package logging
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/bananabytelabs/wazero/api"
    10  	"github.com/bananabytelabs/wazero/internal/logging"
    11  	"github.com/bananabytelabs/wazero/internal/sys"
    12  	. "github.com/bananabytelabs/wazero/internal/wasip1"
    13  )
    14  
    15  var le = binary.LittleEndian
    16  
    17  func isClockFunction(fnd api.FunctionDefinition) bool {
    18  	return strings.HasPrefix(fnd.Name(), "clock_")
    19  }
    20  
    21  func isProcFunction(fnd api.FunctionDefinition) bool {
    22  	return fnd.Name() == ProcExitName
    23  }
    24  
    25  func isFilesystemFunction(fnd api.FunctionDefinition) bool {
    26  	switch {
    27  	case strings.HasPrefix(fnd.Name(), "path_"):
    28  		return true
    29  	case strings.HasPrefix(fnd.Name(), "fd_"):
    30  		return true
    31  	}
    32  	return false
    33  }
    34  
    35  func isPollFunction(fnd api.FunctionDefinition) bool {
    36  	return fnd.Name() == PollOneoffName
    37  }
    38  
    39  func isRandomFunction(fnd api.FunctionDefinition) bool {
    40  	return fnd.Name() == RandomGetName
    41  }
    42  
    43  func isSockFunction(fnd api.FunctionDefinition) bool {
    44  	return strings.HasPrefix(fnd.Name(), "sock_")
    45  }
    46  
    47  // IsInLogScope returns true if the current function is in any of the scopes.
    48  func IsInLogScope(fnd api.FunctionDefinition, scopes logging.LogScopes) bool {
    49  	if scopes.IsEnabled(logging.LogScopeClock) {
    50  		if isClockFunction(fnd) {
    51  			return true
    52  		}
    53  	}
    54  
    55  	if scopes.IsEnabled(logging.LogScopeProc) {
    56  		if isProcFunction(fnd) {
    57  			return true
    58  		}
    59  	}
    60  
    61  	if scopes.IsEnabled(logging.LogScopeFilesystem) {
    62  		if isFilesystemFunction(fnd) {
    63  			return true
    64  		}
    65  	}
    66  
    67  	if scopes.IsEnabled(logging.LogScopePoll) {
    68  		if isPollFunction(fnd) {
    69  			return true
    70  		}
    71  	}
    72  
    73  	if scopes.IsEnabled(logging.LogScopeRandom) {
    74  		if isRandomFunction(fnd) {
    75  			return true
    76  		}
    77  	}
    78  
    79  	if scopes.IsEnabled(logging.LogScopeSock) {
    80  		if isSockFunction(fnd) {
    81  			return true
    82  		}
    83  	}
    84  
    85  	return scopes == logging.LogScopeAll
    86  }
    87  
    88  func Config(fnd api.FunctionDefinition) (pSampler logging.ParamSampler, pLoggers []logging.ParamLogger, rLoggers []logging.ResultLogger) {
    89  	types := fnd.ParamTypes()
    90  	names := fnd.ParamNames()
    91  
    92  	switch fnd.Name() {
    93  	case FdPrestatGetName:
    94  		pLoggers = []logging.ParamLogger{logging.NewParamLogger(0, "fd", logging.ValueTypeI32)}
    95  		rLoggers = []logging.ResultLogger{resultParamLogger("prestat", logPrestat(1).Log), logErrno}
    96  		return
    97  	case ProcExitName:
    98  		pLoggers, rLoggers = logging.Config(fnd)
    99  		return
   100  	case FdReadName, FdWriteName:
   101  		pSampler = fdReadWriteSampler
   102  	}
   103  
   104  	for idx := uint32(0); idx < uint32(len(types)); idx++ {
   105  		name := names[idx]
   106  		var logger logging.ParamLogger
   107  
   108  		if isLookupFlags(fnd, name) {
   109  			lf := &logLookupflags{name, idx}
   110  			logger = lf.Log
   111  			pLoggers = append(pLoggers, logger)
   112  			continue
   113  		}
   114  
   115  		isResult := strings.HasPrefix(name, "result.")
   116  
   117  		if strings.Contains(name, "path") {
   118  			if isResult {
   119  				name = resultParamName(name)
   120  				logger = logString(idx).Log
   121  				rLoggers = append(rLoggers, resultParamLogger(name, logger))
   122  			} else {
   123  				logger = logging.NewParamLogger(idx, name, logging.ValueTypeString)
   124  				pLoggers = append(pLoggers, logger)
   125  			}
   126  			idx++
   127  			continue
   128  		}
   129  
   130  		if strings.HasPrefix(fnd.Name(), "clock_") {
   131  			switch name {
   132  			case "id":
   133  				logger = logClockId(idx).Log
   134  			case "result.resolution":
   135  				name = resultParamName(name)
   136  				logger = logMemI32(idx).Log
   137  				rLoggers = append(rLoggers, resultParamLogger(name, logger))
   138  				continue
   139  			case "result.timestamp":
   140  				name = resultParamName(name)
   141  				logger = logMemI64(idx).Log
   142  				rLoggers = append(rLoggers, resultParamLogger(name, logger))
   143  				continue
   144  			default:
   145  				logger = logging.NewParamLogger(idx, name, types[idx])
   146  			}
   147  			pLoggers = append(pLoggers, logger)
   148  			continue
   149  		}
   150  
   151  		if strings.HasPrefix(fnd.Name(), "sock_") {
   152  			switch name {
   153  			case "flags":
   154  				logger = logFlags(idx).Log
   155  			case "ri_flags":
   156  				logger = logRiFlags(idx).Log
   157  			case "si_flags":
   158  				logger = logSiFlags(idx).Log
   159  			case "how":
   160  				logger = logSdFlags(idx).Log
   161  			case "result.fd", "result.ro_datalen", "result.so_datalen":
   162  				name = resultParamName(name)
   163  				logger = logMemI32(idx).Log
   164  				rLoggers = append(rLoggers, resultParamLogger(name, logger))
   165  				continue
   166  			case "result.ro_flags":
   167  				logger = logRoFlags(idx).Log
   168  				rLoggers = append(rLoggers, resultParamLogger("ro_flags", logger))
   169  				continue
   170  			default:
   171  				logger = logging.NewParamLogger(idx, name, types[idx])
   172  			}
   173  			pLoggers = append(pLoggers, logger)
   174  			continue
   175  		}
   176  
   177  		switch name {
   178  		case "fdflags":
   179  			logger = logFdflags(idx).Log
   180  		case "flags":
   181  			logger = logFlags(idx).Log
   182  		case "fst_flags":
   183  			logger = logFstflags(idx).Log
   184  		case "oflags":
   185  			logger = logOflags(idx).Log
   186  		case "fs_rights_base":
   187  			logger = logFsRightsBase(idx).Log
   188  		case "fs_rights_inheriting":
   189  			logger = logFsRightsInheriting(idx).Log
   190  		case "result.nread", "result.nwritten", "result.opened_fd", "result.nevents", "result.bufused":
   191  			name = resultParamName(name)
   192  			logger = logMemI32(idx).Log
   193  			rLoggers = append(rLoggers, resultParamLogger(name, logger))
   194  			continue
   195  		case "result.newoffset":
   196  			name = resultParamName(name)
   197  			logger = logMemI64(idx).Log
   198  			rLoggers = append(rLoggers, resultParamLogger(name, logger))
   199  			continue
   200  		case "result.filestat":
   201  			name = resultParamName(name)
   202  			logger = logFilestat(idx).Log
   203  			rLoggers = append(rLoggers, resultParamLogger(name, logger))
   204  			continue
   205  		case "result.stat":
   206  			name = resultParamName(name)
   207  			logger = logFdstat(idx).Log
   208  			rLoggers = append(rLoggers, resultParamLogger(name, logger))
   209  			continue
   210  		default:
   211  			logger = logging.NewParamLogger(idx, name, types[idx])
   212  		}
   213  		pLoggers = append(pLoggers, logger)
   214  	}
   215  	// All WASI functions except proc_after return only an logErrno result.
   216  	rLoggers = append(rLoggers, logErrno)
   217  	return
   218  }
   219  
   220  // Ensure we don't clutter log with reads and writes to stdio.
   221  func fdReadWriteSampler(_ context.Context, _ api.Module, params []uint64) bool {
   222  	fd := int32(params[0])
   223  	return fd > sys.FdStderr
   224  }
   225  
   226  func isLookupFlags(fnd api.FunctionDefinition, name string) bool {
   227  	switch fnd.Name() {
   228  	case PathFilestatGetName, PathFilestatSetTimesName:
   229  		return name == "flags"
   230  	case PathLinkName:
   231  		return name == "old_flags"
   232  	case PathOpenName:
   233  		return name == "dirflags"
   234  	}
   235  	return false
   236  }
   237  
   238  func logErrno(_ context.Context, _ api.Module, w logging.Writer, _, results []uint64) {
   239  	errno := ErrnoName(uint32(results[0]))
   240  	w.WriteString("errno=") //nolint
   241  	w.WriteString(errno)    //nolint
   242  }
   243  
   244  type logMemI32 uint32
   245  
   246  func (i logMemI32) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   247  	if v, ok := mod.Memory().ReadUint32Le(uint32(params[i])); ok {
   248  		writeI32(w, v)
   249  	}
   250  }
   251  
   252  type logMemI64 uint32
   253  
   254  func (i logMemI64) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   255  	if v, ok := mod.Memory().ReadUint64Le(uint32(params[i])); ok {
   256  		writeI64(w, v)
   257  	}
   258  }
   259  
   260  type logFilestat uint32
   261  
   262  func (i logFilestat) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   263  	offset, byteCount := uint32(params[i]), uint32(64)
   264  	if buf, ok := mod.Memory().Read(offset, byteCount); ok {
   265  		w.WriteString("{filetype=")          //nolint
   266  		w.WriteString(FiletypeName(buf[16])) //nolint
   267  		w.WriteString(",size=")              //nolint
   268  		writeI64(w, le.Uint64(buf[32:]))
   269  		w.WriteString(",mtim=") //nolint
   270  		writeI64(w, le.Uint64(buf[48:]))
   271  		w.WriteString("}") //nolint
   272  	}
   273  }
   274  
   275  type logFdstat uint32
   276  
   277  func (i logFdstat) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   278  	offset, byteCount := uint32(params[i]), uint32(24)
   279  	if buf, ok := mod.Memory().Read(offset, byteCount); ok {
   280  		w.WriteString("{filetype=")                           //nolint
   281  		w.WriteString(FiletypeName(buf[0]))                   //nolint
   282  		w.WriteString(",fdflags=")                            //nolint
   283  		w.WriteString(FdFlagsString(int(le.Uint16(buf[2:])))) //nolint
   284  		w.WriteString(",fs_rights_base=")                     //nolint
   285  		w.WriteString(RightsString(int(le.Uint16(buf[8:]))))  //nolint
   286  		w.WriteString(",fs_rights_inheriting=")               //nolint
   287  		w.WriteString(RightsString(int(le.Uint16(buf[16:])))) //nolint
   288  		w.WriteString("}")                                    //nolint
   289  	}
   290  }
   291  
   292  type logString uint32
   293  
   294  func (i logString) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   295  	offset, byteCount := uint32(params[i]), uint32(params[i+1])
   296  	if s, ok := mod.Memory().Read(offset, byteCount); ok {
   297  		w.Write(s) //nolint
   298  	}
   299  }
   300  
   301  type logPrestat uint32
   302  
   303  // Log writes the only valid field: pr_name_len
   304  // See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#prestat_dir
   305  func (i logPrestat) Log(_ context.Context, mod api.Module, w logging.Writer, params []uint64) {
   306  	offset := uint32(params[i]) + 4 // skip to pre_name_len field
   307  	if nameLen, ok := mod.Memory().ReadUint32Le(offset); ok {
   308  		w.WriteString("{pr_name_len=") //nolint
   309  		writeI32(w, nameLen)
   310  		w.WriteString("}") //nolint
   311  	}
   312  }
   313  
   314  // resultParamLogger logs the value of the parameter on ESUCCESS.
   315  func resultParamLogger(name string, pLogger logging.ParamLogger) logging.ResultLogger {
   316  	prefix := name + "="
   317  	return func(ctx context.Context, mod api.Module, w logging.Writer, params, results []uint64) {
   318  		w.WriteString(prefix) //nolint
   319  		if Errno(results[0]) == ErrnoSuccess {
   320  			pLogger(ctx, mod, w, params)
   321  		}
   322  	}
   323  }
   324  
   325  type logClockId int
   326  
   327  func (i logClockId) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   328  	id := uint32(params[i])
   329  	w.WriteString("id=") //nolint
   330  	switch id {
   331  	case ClockIDRealtime:
   332  		w.WriteString("realtime") //nolint
   333  	case ClockIDMonotonic:
   334  		w.WriteString("monotonic") //nolint
   335  	default:
   336  		writeI32(w, id)
   337  	}
   338  }
   339  
   340  type logFdflags int
   341  
   342  func (i logFdflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   343  	w.WriteString("fdflags=")                    //nolint
   344  	w.WriteString(FdFlagsString(int(params[i]))) //nolint
   345  }
   346  
   347  type logLookupflags struct {
   348  	name string
   349  	i    uint32
   350  }
   351  
   352  func (l *logLookupflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   353  	w.WriteString(l.name)                              //nolint
   354  	w.WriteByte('=')                                   //nolint
   355  	w.WriteString(LookupflagsString(int(params[l.i]))) //nolint
   356  }
   357  
   358  type logFsRightsBase uint32
   359  
   360  func (i logFsRightsBase) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   361  	w.WriteString("fs_rights_base=")            //nolint
   362  	w.WriteString(RightsString(int(params[i]))) //nolint
   363  }
   364  
   365  type logFsRightsInheriting uint32
   366  
   367  func (i logFsRightsInheriting) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   368  	w.WriteString("fs_rights_inheriting=")      //nolint
   369  	w.WriteString(RightsString(int(params[i]))) //nolint
   370  }
   371  
   372  type logOflags int
   373  
   374  func (i logOflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   375  	w.WriteString("oflags=")                    //nolint
   376  	w.WriteString(OflagsString(int(params[i]))) //nolint
   377  }
   378  
   379  type logFstflags int
   380  
   381  func (i logFstflags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   382  	w.WriteString("fst_flags=")                   //nolint
   383  	w.WriteString(FstflagsString(int(params[i]))) //nolint
   384  }
   385  
   386  type logFlags int
   387  
   388  func (i logFlags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   389  	w.WriteString("flags=")                      //nolint
   390  	w.WriteString(FdFlagsString(int(params[i]))) //nolint
   391  }
   392  
   393  type logSdFlags int
   394  
   395  func (i logSdFlags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   396  	w.WriteString("how=")                        //nolint
   397  	w.WriteString(SdFlagsString(int(params[i]))) //nolint
   398  }
   399  
   400  type logSiFlags int
   401  
   402  func (i logSiFlags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   403  	w.WriteString("si_flags=")                   //nolint
   404  	w.WriteString(SiFlagsString(int(params[i]))) //nolint
   405  }
   406  
   407  type logRiFlags int
   408  
   409  func (i logRiFlags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   410  	w.WriteString("ri_flags=")                   //nolint
   411  	w.WriteString(RiFlagsString(int(params[i]))) //nolint
   412  }
   413  
   414  type logRoFlags int
   415  
   416  func (i logRoFlags) Log(_ context.Context, _ api.Module, w logging.Writer, params []uint64) {
   417  	w.WriteString(RoFlagsString(int(params[i]))) //nolint
   418  }
   419  
   420  func resultParamName(name string) string {
   421  	return name[7:] // without "result."
   422  }
   423  
   424  func writeI32(w logging.Writer, v uint32) {
   425  	w.WriteString(strconv.FormatInt(int64(int32(v)), 10)) //nolint
   426  }
   427  
   428  func writeI64(w logging.Writer, v uint64) {
   429  	w.WriteString(strconv.FormatInt(int64(v), 10)) //nolint
   430  }