From d019518d057201de3c8a1eb45a746d52f3876c3a Mon Sep 17 00:00:00 2001 From: tanlang Date: Wed, 12 Apr 2023 09:29:29 +0000 Subject: [PATCH] feat: add new vm exec tracer --- extern/filecoin-ffi | 2 +- pkg/fvm/cbor_gen.go | 391 ------------------ pkg/fvm/fvm.go | 77 +--- pkg/vm/gas/gas_tracker.go | 18 +- pkg/vm/vmcontext/vmcontext.go | 13 + venus-devtool/api-gen/example.go | 4 +- venus-devtool/cborgen/main.go | 13 +- venus-shared/types/cbor_gen.go | 658 +++++++++++++++++++++++++++++++ venus-shared/types/exec.go | 115 ++---- 9 files changed, 717 insertions(+), 574 deletions(-) delete mode 100644 pkg/fvm/cbor_gen.go diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index 5ab95e3d9c..28e3cd44d9 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit 5ab95e3d9c5a9ceb791b485c301212ff7760af8c +Subproject commit 28e3cd44d91681c074aba362d1e5c954db110ad9 diff --git a/pkg/fvm/cbor_gen.go b/pkg/fvm/cbor_gen.go deleted file mode 100644 index 71b053ecd9..0000000000 --- a/pkg/fvm/cbor_gen.go +++ /dev/null @@ -1,391 +0,0 @@ -// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. - -package fvm - -import ( - "fmt" - "io" - "math" - "sort" - - types "github.com/filecoin-project/venus/venus-shared/actors/types" - types1 "github.com/filecoin-project/venus/venus-shared/types" - cid "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" - xerrors "golang.org/x/xerrors" -) - -var _ = xerrors.Errorf -var _ = cid.Undef -var _ = math.E -var _ = sort.Sort - -var lengthBufFvmExecutionTrace = []byte{133} - -func (t *FvmExecutionTrace) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write(lengthBufFvmExecutionTrace); err != nil { - return err - } - - // t.Msg (types.Message) (struct) - if err := t.Msg.MarshalCBOR(cw); err != nil { - return err - } - - // t.MsgRct (types.MessageReceipt) (struct) - if err := t.MsgRct.MarshalCBOR(cw); err != nil { - return err - } - - // t.Error (string) (string) - if len(t.Error) > cbg.MaxLength { - return xerrors.Errorf("Value in field t.Error was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Error))); err != nil { - return err - } - if _, err := io.WriteString(w, string(t.Error)); err != nil { - return err - } - - // t.GasCharges ([]fvm.FvmGasCharge) (slice) - if len(t.GasCharges) > 1000000000 { - return xerrors.Errorf("Slice value in field t.GasCharges was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.GasCharges))); err != nil { - return err - } - for _, v := range t.GasCharges { - if err := v.MarshalCBOR(cw); err != nil { - return err - } - } - - // t.Subcalls ([]fvm.FvmExecutionTrace) (slice) - if len(t.Subcalls) > 1000000000 { - return xerrors.Errorf("Slice value in field t.Subcalls was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Subcalls))); err != nil { - return err - } - for _, v := range t.Subcalls { - if err := v.MarshalCBOR(cw); err != nil { - return err - } - } - return nil -} - -func (t *FvmExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { - *t = FvmExecutionTrace{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajArray { - return fmt.Errorf("cbor input should be of type array") - } - - if extra != 5 { - return fmt.Errorf("cbor input had wrong number of fields") - } - - // t.Msg (types.Message) (struct) - - { - - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - t.Msg = new(types.Message) - if err := t.Msg.UnmarshalCBOR(cr); err != nil { - return xerrors.Errorf("unmarshaling t.Msg pointer: %w", err) - } - } - - } - // t.MsgRct (types.MessageReceipt) (struct) - - { - - b, err := cr.ReadByte() - if err != nil { - return err - } - if b != cbg.CborNull[0] { - if err := cr.UnreadByte(); err != nil { - return err - } - t.MsgRct = new(types1.MessageReceipt) - if err := t.MsgRct.UnmarshalCBOR(cr); err != nil { - return xerrors.Errorf("unmarshaling t.MsgRct pointer: %w", err) - } - } - - } - // t.Error (string) (string) - - { - sval, err := cbg.ReadString(cr) - if err != nil { - return err - } - - t.Error = string(sval) - } - // t.GasCharges ([]fvm.FvmGasCharge) (slice) - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > 1000000000 { - return fmt.Errorf("t.GasCharges: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.GasCharges = make([]FvmGasCharge, extra) - } - - for i := 0; i < int(extra); i++ { - - var v FvmGasCharge - if err := v.UnmarshalCBOR(cr); err != nil { - return err - } - - t.GasCharges[i] = v - } - - // t.Subcalls ([]fvm.FvmExecutionTrace) (slice) - - maj, extra, err = cr.ReadHeader() - if err != nil { - return err - } - - if extra > 1000000000 { - return fmt.Errorf("t.Subcalls: array too large (%d)", extra) - } - - if maj != cbg.MajArray { - return fmt.Errorf("expected cbor array") - } - - if extra > 0 { - t.Subcalls = make([]FvmExecutionTrace, extra) - } - - for i := 0; i < int(extra); i++ { - - var v FvmExecutionTrace - if err := v.UnmarshalCBOR(cr); err != nil { - return err - } - - t.Subcalls[i] = v - } - - return nil -} - -var lengthBufFvmGasCharge = []byte{132} - -func (t *FvmGasCharge) MarshalCBOR(w io.Writer) error { - if t == nil { - _, err := w.Write(cbg.CborNull) - return err - } - - cw := cbg.NewCborWriter(w) - - if _, err := cw.Write(lengthBufFvmGasCharge); err != nil { - return err - } - - // t.Name (string) (string) - if len(t.Name) > cbg.MaxLength { - return xerrors.Errorf("Value in field t.Name was too long") - } - - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { - return err - } - if _, err := io.WriteString(w, string(t.Name)); err != nil { - return err - } - - // t.TotalGas (int64) (int64) - if t.TotalGas >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.TotalGas)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.TotalGas-1)); err != nil { - return err - } - } - - // t.ComputeGas (int64) (int64) - if t.ComputeGas >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.ComputeGas)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.ComputeGas-1)); err != nil { - return err - } - } - - // t.StorageGas (int64) (int64) - if t.StorageGas >= 0 { - if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.StorageGas)); err != nil { - return err - } - } else { - if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.StorageGas-1)); err != nil { - return err - } - } - return nil -} - -func (t *FvmGasCharge) UnmarshalCBOR(r io.Reader) (err error) { - *t = FvmGasCharge{} - - cr := cbg.NewCborReader(r) - - maj, extra, err := cr.ReadHeader() - if err != nil { - return err - } - defer func() { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - }() - - if maj != cbg.MajArray { - return fmt.Errorf("cbor input should be of type array") - } - - if extra != 4 { - return fmt.Errorf("cbor input had wrong number of fields") - } - - // t.Name (string) (string) - - { - sval, err := cbg.ReadString(cr) - if err != nil { - return err - } - - t.Name = string(sval) - } - // t.TotalGas (int64) (int64) - { - maj, extra, err := cr.ReadHeader() - var extraI int64 - if err != nil { - return err - } - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative oveflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - - t.TotalGas = int64(extraI) - } - // t.ComputeGas (int64) (int64) - { - maj, extra, err := cr.ReadHeader() - var extraI int64 - if err != nil { - return err - } - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative oveflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - - t.ComputeGas = int64(extraI) - } - // t.StorageGas (int64) (int64) - { - maj, extra, err := cr.ReadHeader() - var extraI int64 - if err != nil { - return err - } - switch maj { - case cbg.MajUnsignedInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 positive overflow") - } - case cbg.MajNegativeInt: - extraI = int64(extra) - if extraI < 0 { - return fmt.Errorf("int64 negative oveflow") - } - extraI = -1 - extraI - default: - return fmt.Errorf("wrong type for int64 field: %d", maj) - } - - t.StorageGas = int64(extraI) - } - return nil -} diff --git a/pkg/fvm/fvm.go b/pkg/fvm/fvm.go index 36b755f494..ee16a5001d 100644 --- a/pkg/fvm/fvm.go +++ b/pkg/fvm/fvm.go @@ -62,60 +62,6 @@ type FvmExtern struct { // nolint gasPriceSchedule *gas.PricesSchedule } -type FvmGasCharge struct { // nolint - Name string - TotalGas int64 - ComputeGas int64 - StorageGas int64 -} - -// This may eventually become identical to ExecutionTrace, but we can make incremental progress towards that -type FvmExecutionTrace struct { // nolint - Msg *types.Message - MsgRct *types.MessageReceipt - Error string - - GasCharges []FvmGasCharge `cborgen:"maxlen=1000000000"` - Subcalls []FvmExecutionTrace `cborgen:"maxlen=1000000000"` -} - -func (t *FvmExecutionTrace) ToExecutionTrace() types.ExecutionTrace { - if t == nil { - return types.ExecutionTrace{} - } - - ret := types.ExecutionTrace{ - Msg: t.Msg, - MsgRct: t.MsgRct, - Error: t.Error, - Duration: 0, - GasCharges: nil, - Subcalls: nil, // Should be nil when there are no subcalls for backwards compatibility - } - - if len(t.GasCharges) > 0 { - ret.GasCharges = make([]*types.GasTrace, len(t.GasCharges)) - for i, v := range t.GasCharges { - ret.GasCharges[i] = &types.GasTrace{ - Name: v.Name, - TotalGas: v.TotalGas, - ComputeGas: v.ComputeGas, - StorageGas: v.StorageGas, - } - } - } - - if len(t.Subcalls) > 0 { - ret.Subcalls = make([]types.ExecutionTrace, len(t.Subcalls)) - - for i, v := range t.Subcalls { - ret.Subcalls[i] = v.ToExecutionTrace() - } - } - - return ret -} - func (x *FvmExtern) TipsetCid(ctx context.Context, epoch abi.ChainEpoch) (cid.Cid, error) { tsk, err := x.tsGet(ctx, epoch) if err != nil { @@ -496,19 +442,9 @@ func (fvm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*vm.Ret, var et types.ExecutionTrace if len(ret.ExecTraceBytes) != 0 { - var fvmEt FvmExecutionTrace - if err = fvmEt.UnmarshalCBOR(bytes.NewReader(ret.ExecTraceBytes)); err != nil { + if err = et.UnmarshalCBOR(bytes.NewReader(ret.ExecTraceBytes)); err != nil { return nil, fmt.Errorf("failed to unmarshal exectrace: %w", err) } - et = fvmEt.ToExecutionTrace() - } - - // Set the top-level exectrace info from the message and receipt for backwards compatibility - et.Msg = vmMsg - et.MsgRct = &receipt - et.Duration = duration - if aerr != nil { - et.Error = aerr.Error() } applyRet := &vm.Ret{ @@ -574,18 +510,9 @@ func (fvm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg types.ChainMsg) ( var et types.ExecutionTrace if len(ret.ExecTraceBytes) != 0 { - var fvmEt FvmExecutionTrace - if err = fvmEt.UnmarshalCBOR(bytes.NewReader(ret.ExecTraceBytes)); err != nil { + if err = et.UnmarshalCBOR(bytes.NewReader(ret.ExecTraceBytes)); err != nil { return nil, fmt.Errorf("failed to unmarshal exectrace: %w", err) } - et = fvmEt.ToExecutionTrace() - } else { - et.Msg = vmMsg - et.MsgRct = &receipt - et.Duration = duration - if aerr != nil { - et.Error = aerr.Error() - } } applyRet := &vm.Ret{ diff --git a/pkg/vm/gas/gas_tracker.go b/pkg/vm/gas/gas_tracker.go index 5dd381923a..6a7abe3235 100644 --- a/pkg/vm/gas/gas_tracker.go +++ b/pkg/vm/gas/gas_tracker.go @@ -55,8 +55,6 @@ func (t *GasTracker) TryCharge(gasCharge GasCharge) bool { toUse := gasCharge.Total() // code for https://github.com/filecoin-project/venus/issues/4610 if EnableDetailedTracing { - var callers [10]uintptr - cout := 0 // gruntime.Callers(2+skip, callers[:]) now := time.Now() if t.LastGasCharge != nil { @@ -64,26 +62,12 @@ func (t *GasTracker) TryCharge(gasCharge GasCharge) bool { } gasTrace := types.GasTrace{ - Name: gasCharge.Name, - Extra: gasCharge.Extra, + Name: gasCharge.Name, TotalGas: toUse, ComputeGas: gasCharge.ComputeGas, StorageGas: gasCharge.StorageGas, - // TotalVirtualGas: gasCharge.VirtualCompute*GasComputeMulti + gasCharge.VirtualStorage*GasStorageMulti, - TotalVirtualGas: gasCharge.VirtualCompute + gasCharge.VirtualStorage, - VirtualComputeGas: gasCharge.VirtualCompute, - VirtualStorageGas: gasCharge.VirtualStorage, - - Callers: callers[:cout], - } - - if gasTrace.VirtualStorageGas == 0 { - gasTrace.VirtualStorageGas = gasTrace.StorageGas - } - if gasTrace.VirtualComputeGas == 0 { - gasTrace.VirtualComputeGas = gasTrace.ComputeGas } t.ExecutionTrace.GasCharges = append(t.ExecutionTrace.GasCharges, &gasTrace) diff --git a/pkg/vm/vmcontext/vmcontext.go b/pkg/vm/vmcontext/vmcontext.go index 4c5c5c8e71..9fec1d7591 100644 --- a/pkg/vm/vmcontext/vmcontext.go +++ b/pkg/vm/vmcontext/vmcontext.go @@ -31,6 +31,7 @@ import ( ) const MaxCallDepth = 4096 +const CborCodec = 0x51 var vmlog = logging.Logger("vm.context") @@ -195,6 +196,9 @@ func (vm *LegacyVM) normalizeAddress(addr address.Address) (address.Address, boo func (vm *LegacyVM) applyImplicitMessage(imsg VmMessage) (*Ret, error) { // implicit messages gas is tracked separatly and not paid by the miner gasTank := gas.NewGasTracker(constants.BlockGasLimit * 10000) + if imsg.Params != nil { + gasTank.ExecutionTrace.Msg.ParamsCodec = CborCodec + } // the execution of the implicit messages is simpler than full external/actor-actor messages // execution: @@ -231,6 +235,9 @@ func (vm *LegacyVM) applyImplicitMessage(imsg VmMessage) (*Ret, error) { return nil, fmt.Errorf("invalid exit code %d during implicit message execution: From %s, To %s, Method %d, Value %s, Params %v", code, imsg.From, imsg.To, imsg.Method, imsg.Value, imsg.Params) } + if len(ret) > 0 { + gasTank.ExecutionTrace.MsgRct.ReturnCodec = CborCodec + } return &Ret{ GasTracker: gasTank, OutPuts: gas.GasOutputs{}, @@ -260,6 +267,9 @@ func (vm *LegacyVM) applyMessage(msg *types.Message, onChainMsgSize int) (*Ret, // (see: `invocationContext.invoke()` for the dispatch and execution) // initiate gas tracking gasTank := gas.NewGasTracker(msg.GasLimit) + if len(msg.Params) > 0 { + gasTank.ExecutionTrace.Msg.ParamsCodec = CborCodec + } // pre-send // 1. charge for message existence // 2. load sender actor @@ -408,6 +418,9 @@ func (vm *LegacyVM) applyMessage(msg *types.Message, onChainMsgSize int) (*Ret, code = exitcode.SysErrOutOfGas ret = []byte{} } + if len(ret) > 0 { + gasTank.ExecutionTrace.MsgRct.ReturnCodec = CborCodec + } // Roll back all stateView if the receipt's exit code is not ok. // This is required in addition To revert within the invocation context since top level messages can fail for diff --git a/venus-devtool/api-gen/example.go b/venus-devtool/api-gen/example.go index 721219a26a..f3588ae793 100644 --- a/venus-devtool/api-gen/example.go +++ b/venus-devtool/api-gen/example.go @@ -152,8 +152,8 @@ func init() { addExample(map[string]time.Time{"name": time.Unix(1615243938, 0).UTC()}) addExample(map[string]cid.Cid{}) addExample(&types.ExecutionTrace{ - Msg: ExampleValue("init", reflect.TypeOf(&types.Message{}), nil).(*types.Message), - MsgRct: ExampleValue("init", reflect.TypeOf(&types.MessageReceipt{}), nil).(*types.MessageReceipt), + Msg: ExampleValue("init", reflect.TypeOf(types.MessageTrace{}), nil).(types.MessageTrace), + MsgRct: ExampleValue("init", reflect.TypeOf(types.ReturnTrace{}), nil).(types.ReturnTrace), }) addExample(map[string]types.Actor{ "t01236": ExampleValue("init", reflect.TypeOf(types.Actor{}), nil).(types.Actor), diff --git a/venus-devtool/cborgen/main.go b/venus-devtool/cborgen/main.go index d2acc1ca5d..8f55628ab0 100644 --- a/venus-devtool/cborgen/main.go +++ b/venus-devtool/cborgen/main.go @@ -5,7 +5,6 @@ import ( "path/filepath" "github.com/filecoin-project/venus/pkg/chain" - "github.com/filecoin-project/venus/pkg/fvm" market1 "github.com/filecoin-project/venus/pkg/market" "github.com/filecoin-project/venus/pkg/net/helloprotocol" "github.com/filecoin-project/venus/pkg/state/tree" @@ -57,6 +56,11 @@ func main() { types.PaymentInfo{}, types.Event{}, types.EventEntry{}, + // Tracing + types.GasTrace{}, + types.MessageTrace{}, + types.ReturnTrace{}, + types.ExecutionTrace{}, }, }, { @@ -113,13 +117,6 @@ func main() { chain.TSState{}, }, }, - { - dir: "../pkg/fvm", - types: []interface{}{ - fvm.FvmExecutionTrace{}, - fvm.FvmGasCharge{}, - }, - }, { dir: "../venus-shared/blockstore", types: []interface{}{ diff --git a/venus-shared/types/cbor_gen.go b/venus-shared/types/cbor_gen.go index d46cf8995a..086b961b18 100644 --- a/venus-shared/types/cbor_gen.go +++ b/venus-shared/types/cbor_gen.go @@ -11,10 +11,12 @@ import ( abi "github.com/filecoin-project/go-state-types/abi" paych "github.com/filecoin-project/go-state-types/builtin/v8/paych" crypto "github.com/filecoin-project/go-state-types/crypto" + exitcode "github.com/filecoin-project/go-state-types/exitcode" proof "github.com/filecoin-project/go-state-types/proof" cid "github.com/ipfs/go-cid" cbg "github.com/whyrusleeping/cbor-gen" xerrors "golang.org/x/xerrors" + time "time" ) var _ = xerrors.Errorf @@ -1493,3 +1495,659 @@ func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { } return nil } + +var lengthBufGasTrace = []byte{133} + +func (t *GasTrace) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufGasTrace); err != nil { + return err + } + + // t.Name (string) (string) + if len(t.Name) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.Name was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Name))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.Name)); err != nil { + return err + } + + // t.TotalGas (int64) (int64) + if t.TotalGas >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.TotalGas)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.TotalGas-1)); err != nil { + return err + } + } + + // t.ComputeGas (int64) (int64) + if t.ComputeGas >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.ComputeGas)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.ComputeGas-1)); err != nil { + return err + } + } + + // t.StorageGas (int64) (int64) + if t.StorageGas >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.StorageGas)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.StorageGas-1)); err != nil { + return err + } + } + + // t.TimeTaken (time.Duration) (int64) + if t.TimeTaken >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.TimeTaken)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.TimeTaken-1)); err != nil { + return err + } + } + return nil +} + +func (t *GasTrace) UnmarshalCBOR(r io.Reader) (err error) { + *t = GasTrace{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 5 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Name (string) (string) + + { + sval, err := cbg.ReadString(cr) + if err != nil { + return err + } + + t.Name = string(sval) + } + // t.TotalGas (int64) (int64) + { + maj, extra, err := cr.ReadHeader() + var extraI int64 + if err != nil { + return err + } + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative oveflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.TotalGas = int64(extraI) + } + // t.ComputeGas (int64) (int64) + { + maj, extra, err := cr.ReadHeader() + var extraI int64 + if err != nil { + return err + } + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative oveflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.ComputeGas = int64(extraI) + } + // t.StorageGas (int64) (int64) + { + maj, extra, err := cr.ReadHeader() + var extraI int64 + if err != nil { + return err + } + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative oveflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.StorageGas = int64(extraI) + } + // t.TimeTaken (time.Duration) (int64) + { + maj, extra, err := cr.ReadHeader() + var extraI int64 + if err != nil { + return err + } + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative oveflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.TimeTaken = time.Duration(extraI) + } + return nil +} + +var lengthBufMessageTrace = []byte{134} + +func (t *MessageTrace) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufMessageTrace); err != nil { + return err + } + + // t.From (address.Address) (struct) + if err := t.From.MarshalCBOR(cw); err != nil { + return err + } + + // t.To (address.Address) (struct) + if err := t.To.MarshalCBOR(cw); err != nil { + return err + } + + // t.Value (big.Int) (struct) + if err := t.Value.MarshalCBOR(cw); err != nil { + return err + } + + // t.Method (abi.MethodNum) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Method)); err != nil { + return err + } + + // t.Params ([]uint8) (slice) + if len(t.Params) > cbg.ByteArrayMaxLen { + return xerrors.Errorf("Byte array in field t.Params was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(t.Params))); err != nil { + return err + } + + if _, err := cw.Write(t.Params[:]); err != nil { + return err + } + + // t.ParamsCodec (uint64) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.ParamsCodec)); err != nil { + return err + } + + return nil +} + +func (t *MessageTrace) UnmarshalCBOR(r io.Reader) (err error) { + *t = MessageTrace{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 6 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.From (address.Address) (struct) + + { + + if err := t.From.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.From: %w", err) + } + + } + // t.To (address.Address) (struct) + + { + + if err := t.To.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.To: %w", err) + } + + } + // t.Value (big.Int) (struct) + + { + + if err := t.Value.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.Value: %w", err) + } + + } + // t.Method (abi.MethodNum) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.Method = abi.MethodNum(extra) + + } + // t.Params ([]uint8) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > cbg.ByteArrayMaxLen { + return fmt.Errorf("t.Params: byte array too large (%d)", extra) + } + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + + if extra > 0 { + t.Params = make([]uint8, extra) + } + + if _, err := io.ReadFull(cr, t.Params[:]); err != nil { + return err + } + // t.ParamsCodec (uint64) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.ParamsCodec = uint64(extra) + + } + return nil +} + +var lengthBufReturnTrace = []byte{131} + +func (t *ReturnTrace) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufReturnTrace); err != nil { + return err + } + + // t.ExitCode (exitcode.ExitCode) (int64) + if t.ExitCode >= 0 { + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.ExitCode)); err != nil { + return err + } + } else { + if err := cw.WriteMajorTypeHeader(cbg.MajNegativeInt, uint64(-t.ExitCode-1)); err != nil { + return err + } + } + + // t.Return ([]uint8) (slice) + if len(t.Return) > cbg.ByteArrayMaxLen { + return xerrors.Errorf("Byte array in field t.Return was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(t.Return))); err != nil { + return err + } + + if _, err := cw.Write(t.Return[:]); err != nil { + return err + } + + // t.ReturnCodec (uint64) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.ReturnCodec)); err != nil { + return err + } + + return nil +} + +func (t *ReturnTrace) UnmarshalCBOR(r io.Reader) (err error) { + *t = ReturnTrace{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 3 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.ExitCode (exitcode.ExitCode) (int64) + { + maj, extra, err := cr.ReadHeader() + var extraI int64 + if err != nil { + return err + } + switch maj { + case cbg.MajUnsignedInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 positive overflow") + } + case cbg.MajNegativeInt: + extraI = int64(extra) + if extraI < 0 { + return fmt.Errorf("int64 negative oveflow") + } + extraI = -1 - extraI + default: + return fmt.Errorf("wrong type for int64 field: %d", maj) + } + + t.ExitCode = exitcode.ExitCode(extraI) + } + // t.Return ([]uint8) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > cbg.ByteArrayMaxLen { + return fmt.Errorf("t.Return: byte array too large (%d)", extra) + } + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + + if extra > 0 { + t.Return = make([]uint8, extra) + } + + if _, err := io.ReadFull(cr, t.Return[:]); err != nil { + return err + } + // t.ReturnCodec (uint64) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.ReturnCodec = uint64(extra) + + } + return nil +} + +var lengthBufExecutionTrace = []byte{132} + +func (t *ExecutionTrace) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufExecutionTrace); err != nil { + return err + } + + // t.Msg (types.MessageTrace) (struct) + if err := t.Msg.MarshalCBOR(cw); err != nil { + return err + } + + // t.MsgRct (types.ReturnTrace) (struct) + if err := t.MsgRct.MarshalCBOR(cw); err != nil { + return err + } + + // t.GasCharges ([]*types.GasTrace) (slice) + if len(t.GasCharges) > 1000000000 { + return xerrors.Errorf("Slice value in field t.GasCharges was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.GasCharges))); err != nil { + return err + } + for _, v := range t.GasCharges { + if err := v.MarshalCBOR(cw); err != nil { + return err + } + } + + // t.Subcalls ([]types.ExecutionTrace) (slice) + if len(t.Subcalls) > 1000000000 { + return xerrors.Errorf("Slice value in field t.Subcalls was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Subcalls))); err != nil { + return err + } + for _, v := range t.Subcalls { + if err := v.MarshalCBOR(cw); err != nil { + return err + } + } + return nil +} + +func (t *ExecutionTrace) UnmarshalCBOR(r io.Reader) (err error) { + *t = ExecutionTrace{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 4 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Msg (types.MessageTrace) (struct) + + { + + if err := t.Msg.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.Msg: %w", err) + } + + } + // t.MsgRct (types.ReturnTrace) (struct) + + { + + if err := t.MsgRct.UnmarshalCBOR(cr); err != nil { + return xerrors.Errorf("unmarshaling t.MsgRct: %w", err) + } + + } + // t.GasCharges ([]*types.GasTrace) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > 1000000000 { + return fmt.Errorf("t.GasCharges: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.GasCharges = make([]*GasTrace, extra) + } + + for i := 0; i < int(extra); i++ { + + var v GasTrace + if err := v.UnmarshalCBOR(cr); err != nil { + return err + } + + t.GasCharges[i] = &v + } + + // t.Subcalls ([]types.ExecutionTrace) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > 1000000000 { + return fmt.Errorf("t.Subcalls: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.Subcalls = make([]ExecutionTrace, extra) + } + + for i := 0; i < int(extra); i++ { + + var v ExecutionTrace + if err := v.UnmarshalCBOR(cr); err != nil { + return err + } + + t.Subcalls[i] = v + } + + return nil +} diff --git a/venus-shared/types/exec.go b/venus-shared/types/exec.go index c9ad82b823..9063cf7014 100644 --- a/venus-shared/types/exec.go +++ b/venus-shared/types/exec.go @@ -2,105 +2,60 @@ package types import ( "encoding/json" - "fmt" - "regexp" - "runtime" - "strings" "time" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/exitcode" ) type ExecutionTrace struct { - Msg *Message - MsgRct *MessageReceipt - Error string - Duration time.Duration - GasCharges []*GasTrace - - Subcalls []ExecutionTrace + Msg MessageTrace + MsgRct ReturnTrace + GasCharges []*GasTrace `cborgen:"maxlen=1000000000"` + Subcalls []ExecutionTrace `cborgen:"maxlen=1000000000"` } type GasTrace struct { - Name string - - Location []Loc `json:"loc"` - TotalGas int64 `json:"tg"` - ComputeGas int64 `json:"cg"` - StorageGas int64 `json:"sg"` - TotalVirtualGas int64 `json:"vtg"` - VirtualComputeGas int64 `json:"vcg"` - VirtualStorageGas int64 `json:"vsg"` - - TimeTaken time.Duration `json:"tt"` - Extra interface{} `json:"ex,omitempty"` - - Callers []uintptr `json:"-"` + Name string + TotalGas int64 `json:"tg"` + ComputeGas int64 `json:"cg"` + StorageGas int64 `json:"sg"` + TimeTaken time.Duration `json:"tt"` } -type Loc struct { - File string - Line int - Function string +type MessageTrace struct { + From address.Address + To address.Address + Value abi.TokenAmount + Method abi.MethodNum + Params []byte + ParamsCodec uint64 } -// TODO: ??? -// func (l Loc) Show() bool { -// ignorePrefix := []string{ -// "reflect.", -// "github.com/filecoin-project/lotus/chain/vm.(*Invoker).transform", -// "github.com/filecoin-project/go-amt-ipld/", -// } -// for _, pre := range ignorePrefix { -// if strings.HasPrefix(l.Function, pre) { -// return false -// } -// } -// return true -// } - -func (l Loc) String() string { - file := strings.Split(l.File, "/") - - fn := strings.Split(l.Function, "/") - var fnpkg string - if len(fn) > 2 { - fnpkg = strings.Join(fn[len(fn)-2:], "/") - } else { - fnpkg = l.Function - } +type ReturnTrace struct { + ExitCode exitcode.ExitCode + Return []byte + ReturnCodec uint64 +} - return fmt.Sprintf("%s@%s:%d", fnpkg, file[len(file)-1], l.Line) +func (et ExecutionTrace) SumGas() GasTrace { + return SumGas(et.GasCharges) } -var importantRegex = regexp.MustCompile(`github.com/filecoin-project/specs-actors/(v\d+/)?actors/builtin`) +func SumGas(charges []*GasTrace) GasTrace { + var out GasTrace + for _, gc := range charges { + out.TotalGas += gc.TotalGas + out.ComputeGas += gc.ComputeGas + out.StorageGas += gc.StorageGas + } -func (l Loc) Important() bool { - return importantRegex.MatchString(l.Function) + return out } func (gt *GasTrace) MarshalJSON() ([]byte, error) { type GasTraceCopy GasTrace - if len(gt.Location) == 0 { - if len(gt.Callers) != 0 { - frames := runtime.CallersFrames(gt.Callers) - for { - frame, more := frames.Next() - // TODO: this func name must be fixed - if frame.Function == "github.com/filecoin-project/lotus/chain/vm.(*LegacyVM).ApplyMessage" { - break - } - l := Loc{ - File: frame.File, - Line: frame.Line, - Function: frame.Function, - } - gt.Location = append(gt.Location, l) - if !more { - break - } - } - } - } - cpy := (*GasTraceCopy)(gt) return json.Marshal(cpy) }