diff --git a/cli/state.go b/cli/state.go index acc648c23..47a1624c4 100644 --- a/cli/state.go +++ b/cli/state.go @@ -5,10 +5,12 @@ import ( "context" "encoding/json" "fmt" + "os" "reflect" "sort" "strconv" "strings" + "text/template" "time" "github.com/ipfs/go-cid" @@ -31,6 +33,7 @@ import ( "github.com/filecoin-project/specs-actors/actors/builtin/power" "github.com/filecoin-project/specs-actors/actors/builtin/reward" "github.com/filecoin-project/specs-actors/actors/builtin/verifreg" + "github.com/filecoin-project/specs-actors/actors/runtime/exitcode" "github.com/filecoin-project/specs-actors/actors/util/adt" "github.com/filecoin-project/lotus/api" @@ -40,7 +43,7 @@ import ( ) type methodMeta struct { - name string + Name string params reflect.Type ret reflect.Type @@ -68,7 +71,7 @@ func init() { nf := rt.NumField() methods[c] = append(methods[c], methodMeta{ - name: "Send", + Name: "Send", params: reflect.TypeOf(new(adt.EmptyValue)), ret: reflect.TypeOf(new(adt.EmptyValue)), }) @@ -78,7 +81,7 @@ func init() { export := reflect.TypeOf(exports[i+1]) methods[c] = append(methods[c], methodMeta{ - name: rt.Field(i).Name, + Name: rt.Field(i).Name, params: export.In(1), ret: export.Out(0), }) @@ -923,7 +926,7 @@ var stateComputeStateCmd = &cli.Command{ return c.Code, nil } - return computeStateHtml(ts, stout, getCode) + return computeStateHtmlT(ts, stout, getCode) } fmt.Println("computed state cid: ", stout.Root) @@ -944,16 +947,8 @@ func printInternalExecutions(prefix string, trace []types.ExecutionTrace) { } } -func codeStr(c cid.Cid) string { - cmh, err := multihash.Decode(c.Hash()) - if err != nil { - panic(err) - } - return string(cmh.Digest) -} - -func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error { - fmt.Printf(` +var compStateTemplate = ` + -
Tipset: %s
-
Height: %d
-
State CID: %s
-
Calls
`, ts.Key(), ts.Height(), o.Root) +
Tipset: {{.TipSet.Key}}
+
Epoch: {{.TipSet.Height}}
+
State CID: {{.Comp.Root}}
+
Calls
+ {{range .Comp.Trace}} + {{template "message" (Call .ExecutionTrace false .Msg.Cid.String)}} + {{end}} + + +` - for _, ir := range o.Trace { - toCode, err := getCode(ir.Msg.To) - if err != nil { - return xerrors.Errorf("getting code for %s: %w", toCode, err) - } +var compStateMsg = ` +
+ {{$code := GetCode .Msg.To}} + {{$h := "h2"}}{{if .Subcall}}{{$h = "h4"}}{{end}} - params, err := jsonParams(toCode, ir.Msg.Method, ir.Msg.Params) - if err != nil { - return xerrors.Errorf("decoding params: %w", err) - } +
<{{$h}} class="call">{{CodeStr $code}}:{{GetMethod ($code) (.Msg.Method)}}
+
{{.Msg.From}} -> {{.Msg.To}} ({{ToFil .Msg.Value}} FIL), M{{.Msg.Method}}
+ {{if not .Subcall}}
Msg CID: {{.Msg.Cid}}
{{end}} + {{if gt (len .Msg.Params) 0}} +
{{JsonParams ($code) (.Msg.Method) (.Msg.Params) | html}}
+ {{end}} +
Took {{.Duration}}, Exit: {{.MsgRct.ExitCode}}{{if gt (len .MsgRct.Return) 0}}, Return{{end}}
+ + {{if gt (len .MsgRct.Return) 0}} +
{{JsonReturn ($code) (.Msg.Method) (.MsgRct.Return) | html}}
+ {{end}} - if len(ir.Msg.Params) != 0 { - params = `
` + params + `
` - } else { - params = "" - } + {{if ne .MsgRct.ExitCode 0}} +
Error:
{{.Error}}
+ {{end}} - ret, err := jsonReturn(toCode, ir.Msg.Method, ir.MsgRct.Return) - if err != nil { - return xerrors.Errorf("decoding return value: %w", err) - } +
+ Gas Trace + + + {{range .GasCharges}} + + {{end}} + {{with SumGas .GasCharges}} + + {{end}} +
NameTotal/Compute/StorageTime TakenLocation
{{.Name}}{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}{{.TimeTaken}}{{.Location}}
Sum{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}{{.TimeTaken}}
+
+ {{if gt (len .Subcalls) 0}} +
Subcalls:
+ {{$hash := .Hash}} + {{range .Subcalls}} + {{template "message" (Call . true (printf "%s-%s" $hash .Msg.Cid.String))}} + {{end}} + {{end}} +
` - if len(ir.MsgRct.Return) == 0 { - ret = "" - } else { - ret = `, Return
` + ret + `
` - } - - slow := ir.Duration > 10*time.Millisecond - veryslow := ir.Duration > 50*time.Millisecond - - cid := ir.Msg.Cid() - - fmt.Printf(`
-

%s:%s

-
%s -> %s (%s FIL), M%d
-
Msg CID: %s
-%s -
Took %s, Exit: %d%s -`, cid, cid, codeStr(toCode), methods[toCode][ir.Msg.Method].name, ir.Msg.From, ir.Msg.To, types.FIL(ir.Msg.Value), ir.Msg.Method, cid, params, slow, veryslow, ir.Duration, ir.MsgRct.ExitCode, ir.MsgRct.ExitCode, ret) - if ir.MsgRct.ExitCode != 0 { - fmt.Printf(`
Error:
%s
`, ir.Error) - } - fmt.Printf("\n
Gas Trace" + - "") - - var sumTotal, sumCompute, sumStorage int64 - var sumTime time.Duration - for _, gc := range ir.ExecutionTrace.GasCharges { - fmt.Printf("", - gc.Name, gc.TotalGas, gc.ComputeGas, gc.StorageGas, gc.TimeTaken, gc.Location) - sumTotal += gc.TotalGas - sumCompute += gc.ComputeGas - sumStorage += gc.StorageGas - sumTime += gc.TimeTaken - } - fmt.Printf("", - "Sum", sumTotal, sumCompute, sumStorage, sumTime, "") - - fmt.Printf("
NameTotal/Compute/StorageTime TakenLocation
%s%d/%d/%d%s%s
%s%d/%d/%d%s%s
\n") - - fmt.Println("
Execution trace:
") - if err := printInternalExecutionsHtml(cid.String(), ir.ExecutionTrace.Subcalls, getCode); err != nil { - return err - } - fmt.Println("
") - } - - fmt.Printf(` -`) - return nil +type compStateHtmlIn struct { + TipSet *types.TipSet + Comp *api.ComputeStateOutput } -func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace, getCode func(addr address.Address) (cid.Cid, error)) error { - for i, im := range trace { - hashName := fmt.Sprintf("%s-r%d", hashName, i) - - toCode, err := getCode(im.Msg.To) - if err != nil { - return xerrors.Errorf("getting code for %s: %w", toCode, err) - } - - params, err := jsonParams(toCode, im.Msg.Method, im.Msg.Params) - if err != nil { - return xerrors.Errorf("decoding params: %w", err) - } - - if len(im.Msg.Params) != 0 { - params = `
` + params + `
` - } else { - params = "" - } - - ret, err := jsonReturn(toCode, im.Msg.Method, im.MsgRct.Return) - if err != nil { - return xerrors.Errorf("decoding return value: %w", err) - } - - if len(im.MsgRct.Return) == 0 { - ret = "
" - } else { - ret = `, Return
` + ret + `
` - } - - slow := im.Duration > 10*time.Millisecond - veryslow := im.Duration > 50*time.Millisecond - - fmt.Printf(`
-

%s:%s

-
%s -> %s (%s FIL), M%d
-%s -
Took %s, Exit: %d%s -`, hashName, hashName, codeStr(toCode), methods[toCode][im.Msg.Method].name, im.Msg.From, im.Msg.To, types.FIL(im.Msg.Value), im.Msg.Method, params, slow, veryslow, im.Duration, im.MsgRct.ExitCode, im.MsgRct.ExitCode, ret) - if im.MsgRct.ExitCode != 0 { - fmt.Printf(`
Error:
%s
`, im.Error) - } - fmt.Printf("\n
Gas Trace" + - "") - var sumTotal, sumCompute, sumStorage int64 - var sumTime time.Duration - for _, gc := range im.GasCharges { - fmt.Printf("", - gc.Name, gc.TotalGas, gc.ComputeGas, gc.StorageGas, gc.TimeTaken, gc.Location) - sumTotal += gc.TotalGas - sumCompute += gc.ComputeGas - sumStorage += gc.StorageGas - sumTime += gc.TimeTaken - } - fmt.Printf("", - "Sum", sumTotal, sumCompute, sumStorage, sumTime, "") - fmt.Printf("
NameTotal/Compute/StorageTime TakenLocation
%s%d/%d/%d%s%s
%s%d/%d/%d%s%s
\n") - if len(im.Subcalls) > 0 { - fmt.Println("
Subcalls:
") - if err := printInternalExecutionsHtml(hashName, im.Subcalls, getCode); err != nil { - return err - } - } - fmt.Println("
") +func computeStateHtmlT(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(addr address.Address) (cid.Cid, error)) error { + t, err := template.New("compute_state").Funcs(map[string]interface{}{ + "GetCode": getCode, + "GetMethod": getMethod, + "ToFil": toFil, + "JsonParams": jsonParams, + "JsonReturn": jsonReturn, + "IsSlow": isSlow, + "IsVerySlow": isVerySlow, + "IntExit": func(i exitcode.ExitCode) int64 { return int64(i) }, + "SumGas": sumGas, + "CodeStr": codeStr, + "Call": call, + }).Parse(compStateTemplate) + if err != nil { + return err + } + t, err = t.New("message").Parse(compStateMsg) + if err != nil { + return err } - return nil + return t.ExecuteTemplate(os.Stdout, "compute_state", &compStateHtmlIn{ + TipSet: ts, + Comp: o, + }) +} + +type callMeta struct { + types.ExecutionTrace + Subcall bool + Hash string +} + +func call(e types.ExecutionTrace, subcall bool, hash string) callMeta { + return callMeta{ + ExecutionTrace: e, + Subcall: subcall, + Hash: hash, + } +} + +func codeStr(c cid.Cid) string { + cmh, err := multihash.Decode(c.Hash()) + if err != nil { + panic(err) + } + return string(cmh.Digest) +} + +func getMethod(code cid.Cid, method abi.MethodNum) string { + return methods[code][method].Name +} + +func toFil(f types.BigInt) types.FIL { + return types.FIL(f) +} + +func isSlow(t time.Duration) bool { + return t > 10*time.Millisecond +} + +func isVerySlow(t time.Duration) bool { + return t > 50*time.Millisecond +} + +func sumGas(changes []*types.GasTrace) types.GasTrace { + var out types.GasTrace + for _, gc := range changes { + out.TotalGas += gc.TotalGas + out.ComputeGas += gc.ComputeGas + out.StorageGas += gc.StorageGas + out.TimeTaken += gc.TimeTaken + } + + return out } func jsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, error) {