fix(api): fix debug_ API oom bug (#284)

* feat: fix oom bug

* feat: format import

* feat: remove unused code

* Update version.go

---------

Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
This commit is contained in:
georgehao 2023-04-19 21:26:29 +08:00 committed by GitHub
parent b22944a621
commit f71fc6794a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 61 deletions

View file

@ -3,26 +3,12 @@ package types
import ( import (
"encoding/json" "encoding/json"
"math/big" "math/big"
"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"
"github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/params"
) )
var (
loggerResPool = sync.Pool{
New: func() interface{} {
// init arrays here; other types are inited with default values
return &StructLogRes{
Stack: []string{},
Memory: []string{},
}
},
}
)
// BlockTrace contains block execution traces and results required for rollers. // BlockTrace contains block execution traces and results required for rollers.
type BlockTrace struct { type BlockTrace struct {
ChainID uint64 `json:"chainID"` ChainID uint64 `json:"chainID"`
@ -97,22 +83,21 @@ 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; // NewStructLogResBasic Basic StructLogRes skeleton, Stack&Memory&Storage&ExtraData are separated from it for GC optimization;
// still need to fill in with Stack&Memory&Storage&ExtraData // 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 { func NewStructLogResBasic(pc uint64, op string, gas, gasCost uint64, depth int, refundCounter uint64, err error) *StructLogRes {
logRes := loggerResPool.Get().(*StructLogRes) logRes := &StructLogRes{
logRes.Pc, logRes.Op, logRes.Gas, logRes.GasCost, logRes.Depth, logRes.RefundCounter = pc, op, gas, gasCost, depth, refundCounter Pc: pc,
Op: op,
Gas: gas,
GasCost: gasCost,
Depth: depth,
RefundCounter: refundCounter,
}
if err != nil { if err != nil {
logRes.Error = err.Error() logRes.Error = err.Error()
} }
runtime.SetFinalizer(logRes, func(logRes *StructLogRes) {
logRes.Stack = logRes.Stack[:0]
logRes.Memory = logRes.Memory[:0]
logRes.Storage = nil
logRes.ExtraData = nil
logRes.Error = ""
loggerResPool.Put(logRes)
})
return logRes return logRes
} }
@ -151,7 +136,7 @@ type AccountWrapper struct {
Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation Storage *StorageWrapper `json:"storage,omitempty"` // StorageWrapper can be empty if irrelated to storage operation
} }
// while key & value can also be retrieved from StructLogRes.Storage, // StorageWrapper while key & value can also be retrieved from StructLogRes.Storage,
// we still stored in here for roller's processing convenience. // we still stored in here for roller's processing convenience.
type StorageWrapper struct { type StorageWrapper struct {
Key string `json:"key,omitempty"` Key string `json:"key,omitempty"`

View file

@ -15,7 +15,7 @@ import (
var _ = (*structLogMarshaling)(nil) var _ = (*structLogMarshaling)(nil)
// MarshalJSON marshals as JSON. // MarshalJSON marshals as JSON.
func (s StructLog) MarshalJSON() ([]byte, error) { func (s *StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct { type StructLog struct {
Pc uint64 `json:"pc"` Pc uint64 `json:"pc"`
Op OpCode `json:"op"` Op OpCode `json:"op"`

View file

@ -22,9 +22,7 @@ import (
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"runtime"
"strings" "strings"
"sync"
"time" "time"
"github.com/holiman/uint256" "github.com/holiman/uint256"
@ -81,26 +79,15 @@ type StructLog struct {
Err error `json:"-"` Err error `json:"-"`
} }
var (
loggerPool = sync.Pool{
New: func() interface{} {
return &StructLog{
// init arrays here; other types are inited with default values
Stack: make([]uint256.Int, 0),
}
},
}
)
func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int, err error) *StructLog { func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int, err error) *StructLog {
structlog := loggerPool.Get().(*StructLog) return &StructLog{
structlog.Pc, structlog.Op, structlog.Gas, structlog.GasCost, structlog.Depth, structlog.Err = pc, op, gas, cost, depth, err Pc: pc,
Op: op,
runtime.SetFinalizer(structlog, func(logger *StructLog) { Gas: gas,
logger.clean() GasCost: cost,
loggerPool.Put(logger) Depth: depth,
}) Err: err,
return structlog }
} }
func (s *StructLog) clean() { func (s *StructLog) clean() {
@ -185,6 +172,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
if cfg != nil { if cfg != nil {
logger.cfg = *cfg logger.cfg = *cfg
} }
return logger return logger
} }
@ -225,7 +213,7 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
stack := scope.Stack stack := scope.Stack
contract := scope.Contract contract := scope.Contract
// create a struct log. // create a struct log.
structlog := NewStructlog(pc, op, gas, cost, depth, opErr) structLog := NewStructlog(pc, op, gas, cost, depth, opErr)
// check if already accumulated the specified number of logs // check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
@ -233,12 +221,12 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
} }
// Copy a snapshot of the current memory state to a new buffer // Copy a snapshot of the current memory state to a new buffer
if l.cfg.EnableMemory { if l.cfg.EnableMemory {
structlog.Memory.Write(memory.Data()) structLog.Memory.Write(memory.Data())
structlog.MemorySize = memory.Len() structLog.MemorySize = memory.Len()
} }
// Copy a snapshot of the current stack state to a new buffer // Copy a snapshot of the current stack state to a new buffer
if !l.cfg.DisableStack { if !l.cfg.DisableStack {
structlog.Stack = append(structlog.Stack, stack.Data()...) structLog.Stack = append(structLog.Stack, stack.Data()...)
} }
var ( var (
recordStorageDetail bool recordStorageDetail bool
@ -262,20 +250,20 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
l.storage[contractAddress] = make(Storage) l.storage[contractAddress] = make(Storage)
} }
l.storage[contractAddress][storageKey] = storageValue l.storage[contractAddress][storageKey] = storageValue
structlog.Storage = l.storage[contractAddress].Copy() structLog.Storage = l.storage[contractAddress].Copy()
if err := traceStorage(l, scope, structlog.getOrInitExtraData()); err != nil { if err := traceStorage(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)
} }
} }
if l.cfg.EnableReturnData { if l.cfg.EnableReturnData {
structlog.ReturnData.Write(rData) structLog.ReturnData.Write(rData)
} }
execFuncList, ok := OpcodeExecs[op] execFuncList, ok := OpcodeExecs[op]
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.getOrInitExtraData()); 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)
} }
} }
@ -283,12 +271,12 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop
// for each "calling" op, pick the caller's state // for each "calling" op, pick the caller's state
switch op { switch op {
case CALL, CALLCODE, STATICCALL, DELEGATECALL, CREATE, CREATE2: case CALL, CALLCODE, STATICCALL, DELEGATECALL, CREATE, CREATE2:
extraData := structlog.getOrInitExtraData() extraData := structLog.getOrInitExtraData()
extraData.Caller = append(extraData.Caller, getWrappedAccountForAddr(l, scope.Contract.Address())) extraData.Caller = append(extraData.Caller, getWrappedAccountForAddr(l, scope.Contract.Address()))
} }
structlog.RefundCounter = l.env.StateDB.GetRefund() structLog.RefundCounter = l.env.StateDB.GetRefund()
l.logs = append(l.logs, structlog) l.logs = append(l.logs, structLog)
} }
func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) { func (l *StructLogger) CaptureStateAfter(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
@ -335,7 +323,7 @@ func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.A
} }
} }
// in CaptureExit phase, a CREATE has its target address's code being set and queryable // CaptureExit phase, a CREATE has its target address's code being set and queryable
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) { func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
stackH := len(l.callStackLogInd) stackH := len(l.callStackLogInd)
if stackH == 0 { if stackH == 0 {

View file

@ -24,7 +24,7 @@ import (
const ( const (
VersionMajor = 3 // Major version component of the current release VersionMajor = 3 // Major version component of the current release
VersionMinor = 1 // Minor version component of the current release VersionMinor = 1 // Minor version component of the current release
VersionPatch = 5 // Patch version component of the current release VersionPatch = 6 // Patch version component of the current release
VersionMeta = "alpha" // Version metadata to append to the version string VersionMeta = "alpha" // Version metadata to append to the version string
) )