fix bug and optimize fill_trace logic for gc (#104)

* fix bug and upgrade trace code

* minor

* refactor (#105)

* move

* add comments

* fix typo

* update comments

* rename extraData()

* update comments

Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
maskpp 2022-05-26 10:35:27 +08:00 committed by GitHub
parent 9199413d21
commit 9b99f2e174
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 88 deletions

View file

@ -1,10 +1,25 @@
package types package types
import ( import (
"runtime"
"sync"
"github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/common/hexutil"
) )
var (
loggerResPool = sync.Pool{
New: func() interface{} {
// init arrays here; other types are inited with default values
return &StructLogRes{
Stack: []string{},
Memory: []string{},
}
},
}
)
// BlockResult contains block execution traces and results required for rollers. // BlockResult contains block execution traces and results required for rollers.
type BlockResult struct { type BlockResult struct {
BlockTrace *BlockTrace `json:"blockTrace"` BlockTrace *BlockTrace `json:"blockTrace"`
@ -25,8 +40,8 @@ type ExecutionResult struct {
// It's exist only when tx is a contract call. // It's exist only when tx is a contract call.
CodeHash *common.Hash `json:"codeHash,omitempty"` CodeHash *common.Hash `json:"codeHash,omitempty"`
// If it is a contract call, the contract code is returned. // If it is a contract call, the contract code is returned.
ByteCode string `json:"byteCode,omitempty"` ByteCode string `json:"byteCode,omitempty"`
StructLogs []StructLogRes `json:"structLogs"` StructLogs []*StructLogRes `json:"structLogs"`
} }
// StructLogRes stores a structured log emitted by the EVM while replaying a // StructLogRes stores a structured log emitted by the EVM while replaying a
@ -37,7 +52,7 @@ type StructLogRes struct {
Gas uint64 `json:"gas"` Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"` GasCost uint64 `json:"gasCost"`
Depth int `json:"depth"` Depth int `json:"depth"`
Error string `json:"error,omitempty"` Error error `json:"error,omitempty"`
Stack []string `json:"stack,omitempty"` Stack []string `json:"stack,omitempty"`
Memory []string `json:"memory,omitempty"` Memory []string `json:"memory,omitempty"`
Storage map[string]string `json:"storage,omitempty"` Storage map[string]string `json:"storage,omitempty"`
@ -45,6 +60,22 @@ type StructLogRes struct {
ExtraData *ExtraData `json:"extraData,omitempty"` ExtraData *ExtraData `json:"extraData,omitempty"`
} }
// Basic StructLogRes skeleton, Stack&Memory&Storage&ExtraData are separated from it for GC optimization;
// still need to fill in with Stack&Memory&Storage&ExtraData
func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int, refundCounter uint64, err error) *StructLogRes {
logRes := loggerResPool.Get().(*StructLogRes)
logRes.Pc, logRes.Op, logRes.Gas, logRes.GasCost, logRes.Depth, logRes.RefundCounter = pc, op, gas, gasCost, depth, refundCounter
logRes.Error = err
runtime.SetFinalizer(logRes, func(logRes *StructLogRes) {
logRes.Stack = logRes.Stack[:0]
logRes.Memory = logRes.Memory[:0]
logRes.Storage = nil
logRes.ExtraData = nil
loggerResPool.Put(logRes)
})
return logRes
}
type ExtraData struct { type ExtraData struct {
// CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to addresss code, stack.nth_last(1) addresss code] // CALL | CALLCODE | DELEGATECALL | STATICCALL: [tx.to addresss code, stack.nth_last(1) addresss code]
CodeList [][]byte `json:"codeList,omitempty"` CodeList [][]byte `json:"codeList,omitempty"`
@ -73,30 +104,3 @@ type StorageProofWrapper struct {
Value string `json:"value,omitempty"` Value string `json:"value,omitempty"`
Proof []string `json:"proof,omitempty"` Proof []string `json:"proof,omitempty"`
} }
// NewExtraData create, init and return ExtraData
func NewExtraData() *ExtraData {
return &ExtraData{
CodeList: make([][]byte, 0),
ProofList: make([]*AccountProofWrapper, 0),
}
}
func (e *ExtraData) Clean() {
e.CodeList = e.CodeList[:0]
e.ProofList = e.ProofList[:0]
}
// SealExtraData doesn't show empty fields.
func (e *ExtraData) SealExtraData() *ExtraData {
if len(e.CodeList) == 0 {
e.CodeList = nil
}
if len(e.ProofList) == 0 {
e.ProofList = nil
}
if e.CodeList == nil && e.ProofList == nil {
return nil
}
return e
}

View file

@ -85,15 +85,14 @@ var (
loggerPool = sync.Pool{ loggerPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return &StructLog{ return &StructLog{
Stack: make([]uint256.Int, 0), // init arrays here; other types are inited with default values
ExtraData: types.NewExtraData(), Stack: make([]uint256.Int, 0),
} }
}, },
} }
) )
func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int) *StructLog { func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int) *StructLog {
structlog := loggerPool.Get().(*StructLog) structlog := loggerPool.Get().(*StructLog)
structlog.Pc, structlog.Op, structlog.Gas, structlog.GasCost, structlog.Depth = pc, op, gas, cost, depth structlog.Pc, structlog.Op, structlog.Gas, structlog.GasCost, structlog.Depth = pc, op, gas, cost, depth
@ -109,7 +108,14 @@ func (s *StructLog) clean() {
s.Stack = s.Stack[:0] s.Stack = s.Stack[:0]
s.ReturnData.Reset() s.ReturnData.Reset()
s.Storage = nil s.Storage = nil
s.ExtraData.Clean() s.ExtraData = nil
}
func (s *StructLog) getOrInitExtraData() *types.ExtraData {
if s.ExtraData == nil {
s.ExtraData = &types.ExtraData{}
}
return s.ExtraData
} }
// overrides for gencodec // overrides for gencodec
@ -236,7 +242,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
l.storage[contractAddress][storageKey] = storageValue l.storage[contractAddress][storageKey] = storageValue
structlog.Storage = l.storage[contractAddress].Copy() structlog.Storage = l.storage[contractAddress].Copy()
if err := traceStorageProof(l, scope, structlog.ExtraData); err != nil { if err := traceStorageProof(l, scope, structlog.getOrInitExtraData()); err != nil {
log.Error("Failed to trace data", "opcode", op.String(), "err", err) log.Error("Failed to trace data", "opcode", op.String(), "err", err)
} }
} }
@ -247,7 +253,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
if ok { if ok {
// execute trace func list. // execute trace func list.
for _, exec := range execFuncList { for _, exec := range execFuncList {
if err = exec(l, scope, structlog.ExtraData); err != nil { if err = exec(l, scope, structlog.getOrInitExtraData()); err != nil {
log.Error("Failed to trace data", "opcode", op.String(), "err", err) log.Error("Failed to trace data", "opcode", op.String(), "err", err)
} }
} }
@ -441,63 +447,28 @@ func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Addre
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
var (
formatPool = sync.Pool{
New: func() interface{} {
return make([]types.StructLogRes, 0, 128)
},
}
)
// FormatLogs formats EVM returned structured logs for json output // FormatLogs formats EVM returned structured logs for json output
func FormatLogs(logs []StructLog) []types.StructLogRes { func FormatLogs(logs []StructLog) []*types.StructLogRes {
formatted := formatPool.Get().([]types.StructLogRes) formatted := make([]*types.StructLogRes, 0, len(logs))
runtime.SetFinalizer(&formatted, func(format *[]types.StructLogRes) {
for _, res := range *format {
res.ExtraData = nil
res.Storage = nil
res.Stack = res.Stack[:0]
res.Memory = res.Memory[:0]
}
formatPool.Put(*format)
})
for index, trace := range logs { for _, trace := range logs {
formatted = append(formatted, types.StructLogRes{ logRes := types.NewStructLogResBasic(trace.Pc, trace.Op.String(), trace.Gas, trace.GasCost, trace.Depth, trace.RefundCounter, trace.Err)
Pc: trace.Pc, for _, stackValue := range trace.Stack {
Op: trace.Op.String(), logRes.Stack = append(logRes.Stack, stackValue.Hex())
Gas: trace.Gas,
GasCost: trace.GasCost,
Depth: trace.Depth,
RefundCounter: trace.RefundCounter,
Error: trace.ErrorString(),
})
if len(trace.Stack) != 0 {
if formatted[index].Stack == nil {
formatted[index].Stack = make([]string, 0, len(trace.Stack))
}
for _, stackValue := range trace.Stack {
formatted[index].Stack = append(formatted[index].Stack, stackValue.Hex())
}
} }
if trace.Memory.Len() != 0 { for i := 0; i+32 <= trace.Memory.Len(); i += 32 {
if formatted[index].Memory == nil { logRes.Memory = append(logRes.Memory, common.Bytes2Hex(trace.Memory.Bytes()[i:i+32]))
formatted[index].Memory = make([]string, 0, (trace.Memory.Len()+31)/32)
}
for i := 0; i+32 <= trace.Memory.Len(); i += 32 {
formatted[index].Memory = append(formatted[index].Memory, common.Bytes2Hex(trace.Memory.Bytes()[i:i+32]))
}
} }
if len(trace.Storage) != 0 { if len(trace.Storage) != 0 {
storage := make(map[string]string) storage := make(map[string]string)
for i, storageValue := range trace.Storage { for i, storageValue := range trace.Storage {
storage[i.Hex()] = storageValue.Hex() storage[i.Hex()] = storageValue.Hex()
} }
formatted[index].Storage = storage logRes.Storage = storage
}
if trace.ExtraData != nil {
formatted[index].ExtraData = trace.ExtraData.SealExtraData()
} }
logRes.ExtraData = trace.ExtraData
formatted = append(formatted, logRes)
} }
return formatted return formatted
} }

View file

@ -217,7 +217,7 @@ func TestTraceCall(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []types.StructLogRes{}, StructLogs: []*types.StructLogRes{},
}, },
}, },
// Standard JSON trace upon the head, plain transfer. // Standard JSON trace upon the head, plain transfer.
@ -234,7 +234,7 @@ func TestTraceCall(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []types.StructLogRes{}, StructLogs: []*types.StructLogRes{},
}, },
}, },
// Standard JSON trace upon the non-existent block, error expects // Standard JSON trace upon the non-existent block, error expects
@ -263,7 +263,7 @@ func TestTraceCall(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []types.StructLogRes{}, StructLogs: []*types.StructLogRes{},
}, },
}, },
// Standard JSON trace upon the pending block // Standard JSON trace upon the pending block
@ -280,7 +280,7 @@ func TestTraceCall(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []types.StructLogRes{}, StructLogs: []*types.StructLogRes{},
}, },
}, },
} }
@ -333,7 +333,7 @@ func TestTraceTransaction(t *testing.T) {
Gas: params.TxGas, Gas: params.TxGas,
Failed: false, Failed: false,
ReturnValue: "", ReturnValue: "",
StructLogs: []types.StructLogRes{}, StructLogs: []*types.StructLogRes{},
}) { }) {
t.Error("Transaction tracing result is different") t.Error("Transaction tracing result is different")
} }