Merge pull request #1991 from filecoin-project/feat/split-gas
Add gas tracing
This commit is contained in:
+1
-1
@@ -857,7 +857,7 @@ func (syncer *Syncer) checkBlockMessages(ctx context.Context, b *types.FullBlock
|
||||
|
||||
// Phase 1: syntactic validation, as defined in the spec
|
||||
minGas := vm.PricelistByEpoch(baseTs.Height()).OnChainMessage(msg.ChainLength())
|
||||
if err := m.ValidForBlockInclusion(minGas); err != nil {
|
||||
if err := m.ValidForBlockInclusion(minGas.Total()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,57 @@
|
||||
package types
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ExecutionTrace struct {
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
Duration time.Duration
|
||||
Msg *Message
|
||||
MsgRct *MessageReceipt
|
||||
Error string
|
||||
Duration time.Duration
|
||||
GasCharges []*GasTrace
|
||||
|
||||
Subcalls []ExecutionTrace
|
||||
}
|
||||
|
||||
type GasTrace struct {
|
||||
Name string
|
||||
Location string
|
||||
TotalGas int64
|
||||
ComputeGas int64
|
||||
StorageGas int64
|
||||
|
||||
TimeTaken time.Duration
|
||||
|
||||
Callers []uintptr `json:"-"`
|
||||
}
|
||||
|
||||
func (gt *GasTrace) MarshalJSON() ([]byte, error) {
|
||||
type GasTraceCopy GasTrace
|
||||
if gt.Location == "" {
|
||||
if len(gt.Callers) != 0 {
|
||||
frames := runtime.CallersFrames(gt.Callers)
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
fn := strings.Split(frame.Function, "/")
|
||||
|
||||
split := strings.Split(frame.File, "/")
|
||||
file := strings.Join(split[len(split)-2:], "/")
|
||||
gt.Location += fmt.Sprintf("%s@%s:%d", fn[len(fn)-1], file, frame.Line)
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
gt.Location += "|"
|
||||
}
|
||||
} else {
|
||||
gt.Location = "n/a"
|
||||
}
|
||||
}
|
||||
|
||||
cpy := (*GasTraceCopy)(gt)
|
||||
return json.Marshal(cpy)
|
||||
}
|
||||
|
||||
+38
-15
@@ -12,34 +12,57 @@ import (
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
const (
|
||||
GasStorageMulti = 1
|
||||
GasComputeMulti = 1
|
||||
)
|
||||
|
||||
type GasCharge struct {
|
||||
Name string
|
||||
ComputeGas int64
|
||||
StorageGas int64
|
||||
}
|
||||
|
||||
func (g GasCharge) Total() int64 {
|
||||
return g.ComputeGas*GasComputeMulti + g.StorageGas*GasStorageMulti
|
||||
}
|
||||
|
||||
func newGasCharge(name string, computeGas int64, storageGas int64) GasCharge {
|
||||
return GasCharge{
|
||||
Name: name,
|
||||
ComputeGas: computeGas,
|
||||
StorageGas: storageGas,
|
||||
}
|
||||
}
|
||||
|
||||
// Pricelist provides prices for operations in the VM.
|
||||
//
|
||||
// Note: this interface should be APPEND ONLY since last chain checkpoint
|
||||
type Pricelist interface {
|
||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
||||
OnChainMessage(msgSize int) int64
|
||||
OnChainMessage(msgSize int) GasCharge
|
||||
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
|
||||
OnChainReturnValue(dataSize int) int64
|
||||
OnChainReturnValue(dataSize int) GasCharge
|
||||
|
||||
// OnMethodInvocation returns the gas used when invoking a method.
|
||||
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64
|
||||
OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge
|
||||
|
||||
// OnIpldGet returns the gas used for storing an object
|
||||
OnIpldGet(dataSize int) int64
|
||||
OnIpldGet(dataSize int) GasCharge
|
||||
// OnIpldPut returns the gas used for storing an object
|
||||
OnIpldPut(dataSize int) int64
|
||||
OnIpldPut(dataSize int) GasCharge
|
||||
|
||||
// OnCreateActor returns the gas used for creating an actor
|
||||
OnCreateActor() int64
|
||||
OnCreateActor() GasCharge
|
||||
// OnDeleteActor returns the gas used for deleting an actor
|
||||
OnDeleteActor() int64
|
||||
OnDeleteActor() GasCharge
|
||||
|
||||
OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error)
|
||||
OnHashing(dataSize int) int64
|
||||
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64
|
||||
OnVerifySeal(info abi.SealVerifyInfo) int64
|
||||
OnVerifyPost(info abi.WindowPoStVerifyInfo) int64
|
||||
OnVerifyConsensusFault() int64
|
||||
OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error)
|
||||
OnHashing(dataSize int) GasCharge
|
||||
OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge
|
||||
OnVerifySeal(info abi.SealVerifyInfo) GasCharge
|
||||
OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge
|
||||
OnVerifyConsensusFault() GasCharge
|
||||
}
|
||||
|
||||
var prices = map[abi.ChainEpoch]Pricelist{
|
||||
@@ -93,7 +116,7 @@ func PricelistByEpoch(epoch abi.ChainEpoch) Pricelist {
|
||||
type pricedSyscalls struct {
|
||||
under vmr.Syscalls
|
||||
pl Pricelist
|
||||
chargeGas func(int64)
|
||||
chargeGas func(GasCharge)
|
||||
}
|
||||
|
||||
// Verifies that a signature is valid for an address and plaintext.
|
||||
@@ -146,6 +169,6 @@ func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte
|
||||
}
|
||||
|
||||
func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) {
|
||||
ps.chargeGas(0) // TODO: this is only called by the cron actor. Should we even charge gas?
|
||||
ps.chargeGas(newGasCharge("BatchVerifySeals", 0, 0)) // TODO: this is only called by the cron actor. Should we even charge gas?
|
||||
return ps.under.BatchVerifySeals(inp)
|
||||
}
|
||||
|
||||
+29
-27
@@ -2,6 +2,7 @@ package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/builtin"
|
||||
"github.com/filecoin-project/specs-actors/actors/crypto"
|
||||
@@ -84,17 +85,17 @@ type pricelistV0 struct {
|
||||
var _ Pricelist = (*pricelistV0)(nil)
|
||||
|
||||
// OnChainMessage returns the gas used for storing a message of a given size in the chain.
|
||||
func (pl *pricelistV0) OnChainMessage(msgSize int) int64 {
|
||||
return pl.onChainMessageBase + pl.onChainMessagePerByte*int64(msgSize)
|
||||
func (pl *pricelistV0) OnChainMessage(msgSize int) GasCharge {
|
||||
return newGasCharge("OnChainMessage", 0, pl.onChainMessageBase+pl.onChainMessagePerByte*int64(msgSize))
|
||||
}
|
||||
|
||||
// OnChainReturnValue returns the gas used for storing the response of a message in the chain.
|
||||
func (pl *pricelistV0) OnChainReturnValue(dataSize int) int64 {
|
||||
return int64(dataSize) * pl.onChainReturnValuePerByte
|
||||
func (pl *pricelistV0) OnChainReturnValue(dataSize int) GasCharge {
|
||||
return newGasCharge("OnChainReturnValue", 0, int64(dataSize)*pl.onChainReturnValuePerByte)
|
||||
}
|
||||
|
||||
// OnMethodInvocation returns the gas used when invoking a method.
|
||||
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) int64 {
|
||||
func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.MethodNum) GasCharge {
|
||||
ret := pl.sendBase
|
||||
if value != abi.NewTokenAmount(0) {
|
||||
ret += pl.sendTransferFunds
|
||||
@@ -102,62 +103,63 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M
|
||||
if methodNum != builtin.MethodSend {
|
||||
ret += pl.sendInvokeMethod
|
||||
}
|
||||
return ret
|
||||
return newGasCharge("OnMethodInvocation", ret, 0)
|
||||
}
|
||||
|
||||
// OnIpldGet returns the gas used for storing an object
|
||||
func (pl *pricelistV0) OnIpldGet(dataSize int) int64 {
|
||||
return pl.ipldGetBase + int64(dataSize)*pl.ipldGetPerByte
|
||||
func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge {
|
||||
return newGasCharge(fmt.Sprintf("OnIpldGet:%db", dataSize), pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0)
|
||||
}
|
||||
|
||||
// OnIpldPut returns the gas used for storing an object
|
||||
func (pl *pricelistV0) OnIpldPut(dataSize int) int64 {
|
||||
return pl.ipldPutBase + int64(dataSize)*pl.ipldPutPerByte
|
||||
func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge {
|
||||
return newGasCharge(fmt.Sprintf("OnIpldPut:%db", dataSize), pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte)
|
||||
}
|
||||
|
||||
// OnCreateActor returns the gas used for creating an actor
|
||||
func (pl *pricelistV0) OnCreateActor() int64 {
|
||||
return pl.createActorBase + pl.createActorExtra
|
||||
func (pl *pricelistV0) OnCreateActor() GasCharge {
|
||||
return newGasCharge("OnCreateActor", pl.createActorBase, pl.createActorExtra)
|
||||
}
|
||||
|
||||
// OnDeleteActor returns the gas used for deleting an actor
|
||||
func (pl *pricelistV0) OnDeleteActor() int64 {
|
||||
return pl.deleteActor
|
||||
func (pl *pricelistV0) OnDeleteActor() GasCharge {
|
||||
return newGasCharge("OnDeleteActor", 0, pl.deleteActor)
|
||||
}
|
||||
|
||||
// OnVerifySignature
|
||||
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (int64, error) {
|
||||
func (pl *pricelistV0) OnVerifySignature(sigType crypto.SigType, planTextSize int) (GasCharge, error) {
|
||||
costFn, ok := pl.verifySignature[sigType]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("cost function for signature type %d not supported", sigType)
|
||||
return GasCharge{}, fmt.Errorf("cost function for signature type %d not supported", sigType)
|
||||
}
|
||||
return costFn(int64(planTextSize)), nil
|
||||
sigName, _ := sigType.Name()
|
||||
return newGasCharge("OnVerifySignature/"+sigName, costFn(int64(planTextSize)), 0), nil
|
||||
}
|
||||
|
||||
// OnHashing
|
||||
func (pl *pricelistV0) OnHashing(dataSize int) int64 {
|
||||
return pl.hashingBase + int64(dataSize)*pl.hashingPerByte
|
||||
func (pl *pricelistV0) OnHashing(dataSize int) GasCharge {
|
||||
return newGasCharge("OnHashing", pl.hashingBase+int64(dataSize)*pl.hashingPerByte, 0)
|
||||
}
|
||||
|
||||
// OnComputeUnsealedSectorCid
|
||||
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) int64 {
|
||||
func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredProof, pieces []abi.PieceInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.computeUnsealedSectorCidBase
|
||||
return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifySeal
|
||||
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) int64 {
|
||||
func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.verifySealBase
|
||||
return newGasCharge("OnVerifySeal", pl.verifySealBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifyPost
|
||||
func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) int64 {
|
||||
func (pl *pricelistV0) OnVerifyPost(info abi.WindowPoStVerifyInfo) GasCharge {
|
||||
// TODO: this needs more cost tunning, check with @lotus
|
||||
return pl.verifyPostBase
|
||||
return newGasCharge("OnVerifyPost", pl.verifyPostBase, 0)
|
||||
}
|
||||
|
||||
// OnVerifyConsensusFault
|
||||
func (pl *pricelistV0) OnVerifyConsensusFault() int64 {
|
||||
return pl.verifyConsensusFault
|
||||
func (pl *pricelistV0) OnVerifyConsensusFault() GasCharge {
|
||||
return newGasCharge("OnVerifyConsensusFault", pl.verifyConsensusFault, 0)
|
||||
}
|
||||
|
||||
+39
-10
@@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
gruntime "runtime"
|
||||
"time"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/specs-actors/actors/abi"
|
||||
@@ -50,10 +52,12 @@ type Runtime struct {
|
||||
origin address.Address
|
||||
originNonce uint64
|
||||
|
||||
executionTrace types.ExecutionTrace
|
||||
numActorsCreated uint64
|
||||
allowInternal bool
|
||||
callerValidated bool
|
||||
executionTrace types.ExecutionTrace
|
||||
numActorsCreated uint64
|
||||
allowInternal bool
|
||||
callerValidated bool
|
||||
lastGasChargeTime time.Time
|
||||
lastGasCharge *types.GasTrace
|
||||
}
|
||||
|
||||
func (rt *Runtime) TotalFilCircSupply() abi.TokenAmount {
|
||||
@@ -392,7 +396,7 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, 0)
|
||||
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil)
|
||||
if errSend != nil {
|
||||
if errRevert := st.Revert(); errRevert != nil {
|
||||
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
|
||||
@@ -484,14 +488,39 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) ChargeGas(toUse int64) {
|
||||
err := rt.chargeGasInternal(toUse)
|
||||
func (rt *Runtime) ChargeGas(gas GasCharge) {
|
||||
err := rt.chargeGasInternal(gas)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
func (rt *Runtime) finilizeGasTracing() {
|
||||
if rt.lastGasCharge != nil {
|
||||
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Runtime) chargeGasInternal(gas GasCharge) aerrors.ActorError {
|
||||
toUse := gas.Total()
|
||||
var callers [3]uintptr
|
||||
cout := gruntime.Callers(3, callers[:])
|
||||
|
||||
now := time.Now()
|
||||
if rt.lastGasCharge != nil {
|
||||
rt.lastGasCharge.TimeTaken = now.Sub(rt.lastGasChargeTime)
|
||||
}
|
||||
|
||||
gasTrace := types.GasTrace{
|
||||
Name: gas.Name,
|
||||
TotalGas: toUse,
|
||||
ComputeGas: gas.ComputeGas,
|
||||
StorageGas: gas.StorageGas,
|
||||
Callers: callers[:cout],
|
||||
}
|
||||
rt.executionTrace.GasCharges = append(rt.executionTrace.GasCharges, &gasTrace)
|
||||
rt.lastGasChargeTime = now
|
||||
rt.lastGasCharge = &gasTrace
|
||||
|
||||
func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
|
||||
if rt.gasUsed+toUse > rt.gasAvailable {
|
||||
rt.gasUsed = rt.gasAvailable
|
||||
return aerrors.Newf(exitcode.SysErrOutOfGas, "not enough gas: used=%d, available=%d", rt.gasUsed, rt.gasAvailable)
|
||||
@@ -500,8 +529,8 @@ func (rt *Runtime) chargeGasInternal(toUse int64) aerrors.ActorError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rt *Runtime) chargeGasSafe(toUse int64) aerrors.ActorError {
|
||||
return rt.chargeGasInternal(toUse)
|
||||
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
|
||||
return rt.chargeGasInternal(gas)
|
||||
}
|
||||
|
||||
func (rt *Runtime) Pricelist() Pricelist {
|
||||
|
||||
+28
-14
@@ -62,7 +62,7 @@ func ResolveToKeyAddr(state types.StateTree, cst cbor.IpldStore, addr address.Ad
|
||||
var _ cbor.IpldBlockstore = (*gasChargingBlocks)(nil)
|
||||
|
||||
type gasChargingBlocks struct {
|
||||
chargeGas func(int64)
|
||||
chargeGas func(GasCharge)
|
||||
pricelist Pricelist
|
||||
under cbor.IpldBlockstore
|
||||
}
|
||||
@@ -96,13 +96,14 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
|
||||
originNonce: originNonce,
|
||||
height: vm.blockHeight,
|
||||
|
||||
gasUsed: usedGas,
|
||||
gasAvailable: msg.GasLimit,
|
||||
numActorsCreated: nac,
|
||||
pricelist: PricelistByEpoch(vm.blockHeight),
|
||||
allowInternal: true,
|
||||
callerValidated: false,
|
||||
executionTrace: types.ExecutionTrace{Msg: msg},
|
||||
gasUsed: usedGas,
|
||||
gasAvailable: msg.GasLimit,
|
||||
numActorsCreated: nac,
|
||||
pricelist: PricelistByEpoch(vm.blockHeight),
|
||||
allowInternal: true,
|
||||
callerValidated: false,
|
||||
executionTrace: types.ExecutionTrace{Msg: msg},
|
||||
lastGasChargeTime: time.Now(),
|
||||
}
|
||||
|
||||
rt.cst = &cbor.BasicIpldStore{
|
||||
@@ -171,17 +172,17 @@ type ApplyRet struct {
|
||||
}
|
||||
|
||||
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
gasCharge int64) ([]byte, aerrors.ActorError, *Runtime) {
|
||||
gasCharge *GasCharge) ([]byte, aerrors.ActorError, *Runtime) {
|
||||
start := time.Now()
|
||||
|
||||
st := vm.cstate
|
||||
gasUsed := gasCharge
|
||||
|
||||
origin := msg.From
|
||||
on := msg.Nonce
|
||||
var nac uint64 = 0
|
||||
var gasUsed int64
|
||||
if parent != nil {
|
||||
gasUsed = parent.gasUsed + gasUsed
|
||||
gasUsed = parent.gasUsed
|
||||
origin = parent.origin
|
||||
on = parent.originNonce
|
||||
nac = parent.numActorsCreated
|
||||
@@ -189,11 +190,22 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
|
||||
|
||||
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
|
||||
if parent != nil {
|
||||
rt.lastGasChargeTime = parent.lastGasChargeTime
|
||||
rt.lastGasCharge = parent.lastGasCharge
|
||||
defer func() {
|
||||
parent.gasUsed = rt.gasUsed
|
||||
parent.lastGasChargeTime = rt.lastGasChargeTime
|
||||
parent.lastGasCharge = rt.lastGasCharge
|
||||
}()
|
||||
}
|
||||
|
||||
if gasCharge != nil {
|
||||
if err := rt.chargeGasSafe(*gasCharge); err != nil {
|
||||
// this should never happen
|
||||
return nil, aerrors.Wrap(err, "not enough gas for initial message charge, this should not happen"), rt
|
||||
}
|
||||
}
|
||||
|
||||
ret, err := func() ([]byte, aerrors.ActorError) {
|
||||
if aerr := rt.chargeGasSafe(rt.Pricelist().OnMethodInvocation(msg.Value, msg.Method)); aerr != nil {
|
||||
return nil, aerrors.Wrap(aerr, "not enough gas for method invocation")
|
||||
@@ -261,7 +273,7 @@ func checkMessage(msg *types.Message) error {
|
||||
|
||||
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
|
||||
start := time.Now()
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, 0)
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, nil)
|
||||
return &ApplyRet{
|
||||
MessageReceipt: types.MessageReceipt{
|
||||
ExitCode: aerrors.RetCode(actorErr),
|
||||
@@ -294,7 +306,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
|
||||
pl := PricelistByEpoch(vm.blockHeight)
|
||||
|
||||
msgGasCost := pl.OnChainMessage(cmsg.ChainLength())
|
||||
msgGas := pl.OnChainMessage(cmsg.ChainLength())
|
||||
msgGasCost := msgGas.Total()
|
||||
// this should never happen, but is currently still exercised by some tests
|
||||
if msgGasCost > msg.GasLimit {
|
||||
return &ApplyRet{
|
||||
@@ -377,7 +390,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
|
||||
}
|
||||
defer st.ClearSnapshot()
|
||||
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, msgGasCost)
|
||||
ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas)
|
||||
rt.finilizeGasTracing()
|
||||
if aerrors.IsFatal(actorErr) {
|
||||
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
|
||||
}
|
||||
|
||||
@@ -975,6 +975,11 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
|
||||
}
|
||||
.slow-true-false { color: #660; }
|
||||
.slow-true-true { color: #f80; }
|
||||
table {
|
||||
font-size: 12px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
tr.sum { border-top: 1px solid black; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1026,6 +1031,23 @@ func computeStateHtml(ts *types.TipSet, o *api.ComputeStateOutput, getCode func(
|
||||
if ir.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, ir.Error)
|
||||
}
|
||||
fmt.Printf("\n<details><summary>Gas Trace</summary><table><tr>" +
|
||||
"<th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr>")
|
||||
|
||||
var sumTotal, sumCompute, sumStorage int64
|
||||
var sumTime time.Duration
|
||||
for _, gc := range ir.ExecutionTrace.GasCharges {
|
||||
fmt.Printf("<tr><td>%s</td><td>%d/%d/%d</td><td>%s</td><td>%s</td></tr>",
|
||||
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("<tr class=\"sum\"><td>%s</td><td>%d/%d/%d</td><td>%s</td><td>%s</td></tr>",
|
||||
"<b>Sum</b>", sumTotal, sumCompute, sumStorage, sumTime, "")
|
||||
|
||||
fmt.Printf("</table></details>\n")
|
||||
|
||||
fmt.Println("<div>Execution trace:</div>")
|
||||
if err := printInternalExecutionsHtml(cid.String(), ir.ExecutionTrace.Subcalls, getCode); err != nil {
|
||||
@@ -1082,6 +1104,21 @@ func printInternalExecutionsHtml(hashName string, trace []types.ExecutionTrace,
|
||||
if im.MsgRct.ExitCode != 0 {
|
||||
fmt.Printf(`<div class="error">Error: <pre>%s</pre></div>`, im.Error)
|
||||
}
|
||||
fmt.Printf("\n<details><summary>Gas Trace</summary><table><tr>" +
|
||||
"<th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr>")
|
||||
var sumTotal, sumCompute, sumStorage int64
|
||||
var sumTime time.Duration
|
||||
for _, gc := range im.GasCharges {
|
||||
fmt.Printf("<tr><td>%s</td><td>%d/%d/%d</td><td>%s</td><td>%s</td></tr>",
|
||||
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("<tr class=\"sum\"><td>%s</td><td>%d/%d/%d</td><td>%s</td><td>%s</td></tr>",
|
||||
"<b>Sum</b>", sumTotal, sumCompute, sumStorage, sumTime, "")
|
||||
fmt.Printf("</table></details>\n")
|
||||
if len(im.Subcalls) > 0 {
|
||||
fmt.Println("<div>Subcalls:</div>")
|
||||
if err := printInternalExecutionsHtml(hashName, im.Subcalls, getCode); err != nil {
|
||||
|
||||
+1
-1
@@ -485,7 +485,7 @@ func SelectMessages(ctx context.Context, al ActorLookup, ts *types.TipSet, msgs
|
||||
vmstart := time.Now()
|
||||
|
||||
minGas := vm.PricelistByEpoch(ts.Height()).OnChainMessage(msg.ChainLength()) // TODO: really should be doing just msg.ChainLength() but the sync side of this code doesnt seem to have access to that
|
||||
if err := msg.VMMessage().ValidForBlockInclusion(minGas); err != nil {
|
||||
if err := msg.VMMessage().ValidForBlockInclusion(minGas.Total()); err != nil {
|
||||
log.Warnf("invalid message in message pool: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user