130 lines
3.0 KiB
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)
|
|
}
|