wasmcloud/executor/executor.go

130 lines
3.0 KiB
Go

package executor
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
"github.com/gosuri/uitable"
"github.com/perlin-network/life/compiler"
"github.com/perlin-network/life/exec"
"github.com/rogpeppe/go-internal/txtar"
"within.website/olin/abi/cwa"
)
var (
ErrNoCWAMain = errors.New("executor: no cwa_main function defined")
)
type Config struct {
exec.VMConfig
Name string
FuncName string
Env map[string]string
Binary []byte
Stdin io.Reader
Stdout, Stderr io.Writer
LogSink io.Writer
}
type Result struct {
Status int64 `json:"status"`
GasUsed uint64 `json:"gas_used"`
SyscallCount int64 `json:"syscall_count"`
ExecDur time.Duration `json:"exec_dur"`
PagesBeforeExec int `json:"pages_before_exec"`
PagesUsed int `json:"pages_used"`
StartTime time.Time `json:"start_time"`
}
func (r Result) StatsJSONFile() txtar.File {
data, err := json.MarshalIndent(r, "", " ")
if err != nil /* unlikely */ {
panic(err)
}
return txtar.File{
Name: "vmstats.json",
Data: data,
}
}
func (r Result) StatsFile() txtar.File {
table := uitable.New()
table.AddRow("Status", fmt.Sprint(r.Status))
table.AddRow("Gas Used", fmt.Sprint(r.GasUsed))
table.AddRow("Syscall Count", fmt.Sprint(r.SyscallCount))
table.AddRow("Exec Duration", r.ExecDur.String())
table.AddRow("Pages Used", fmt.Sprint(r.PagesUsed))
table.AddRow("Page Delta", fmt.Sprint(r.PagesUsed-r.PagesBeforeExec))
return txtar.File{
Name: "vmstats.txt",
Data: []byte(table.String()),
}
}
func Run(c Config) (*Result, error) {
p := cwa.NewProcess(c.Name, nil, c.Env)
p.Stdin = c.Stdin
p.Stdout = c.Stdout
p.Stderr = c.Stderr
p.Logger = log.New(io.MultiWriter(c.LogSink, os.Stdout), c.Name+" ", log.LstdFlags)
p.HC.Transport = WasmcloudBrandingTransport(c.Name, p.HC.Transport)
gp := &compiler.SimpleGasPolicy{GasPerInstruction: 1}
vm, err := exec.NewVirtualMachine(c.Binary, c.VMConfig, p, gp)
if err != nil {
return nil, err
}
main, ok := vm.GetFunctionExport(c.FuncName)
if !ok {
return nil, ErrNoCWAMain
}
pagesBefore := len(vm.Memory) / 65536
begin := time.Now()
ret, err := vm.Run(main)
if err != nil {
return nil, err
}
dur := time.Since(begin)
p.Logger.Printf("[SYSTEM] return status: %v", ret)
return &Result{
Status: ret,
GasUsed: vm.Gas,
SyscallCount: p.SyscallCount(),
ExecDur: dur,
PagesBeforeExec: pagesBefore,
PagesUsed: len(vm.Memory) / 65536,
StartTime: begin,
}, nil
}
func WasmcloudBrandingTransport(handlerName string, rt http.RoundTripper) http.RoundTripper {
return wasmcloudBrandingTransport{
rt: rt,
handlerName: handlerName,
}
}
type wasmcloudBrandingTransport struct {
rt http.RoundTripper
handlerName string
}
func (w wasmcloudBrandingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
r.Header.Set("User-Agent", r.Header.Get("User-Agent")+" wasmcloud/"+w.handlerName)
return w.rt.RoundTrip(r)
}