diff --git a/core/types/l2trace.go b/core/types/l2trace.go index 920844d68f..2f2be6aa3e 100644 --- a/core/types/l2trace.go +++ b/core/types/l2trace.go @@ -80,6 +80,11 @@ func NewExtraData() *ExtraData { } } +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 { diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go index 261243288f..16d08ae488 100644 --- a/core/vm/gen_structlog.go +++ b/core/vm/gen_structlog.go @@ -37,10 +37,10 @@ func (s StructLog) MarshalJSON() ([]byte, error) { enc.Op = s.Op enc.Gas = math.HexOrDecimal64(s.Gas) enc.GasCost = math.HexOrDecimal64(s.GasCost) - enc.Memory = s.Memory + enc.Memory = s.Memory.Bytes() enc.MemorySize = s.MemorySize enc.Stack = s.Stack - enc.ReturnData = s.ReturnData + enc.ReturnData = s.ReturnData.Bytes() enc.Storage = s.Storage enc.Depth = s.Depth enc.RefundCounter = s.RefundCounter @@ -83,7 +83,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.GasCost = uint64(*dec.GasCost) } if dec.Memory != nil { - s.Memory = *dec.Memory + s.Memory.Write(*dec.Memory) } if dec.MemorySize != nil { s.MemorySize = *dec.MemorySize @@ -92,7 +92,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error { s.Stack = dec.Stack } if dec.ReturnData != nil { - s.ReturnData = *dec.ReturnData + s.ReturnData.Write(*dec.ReturnData) } if dec.Storage != nil { s.Storage = dec.Storage diff --git a/core/vm/logger.go b/core/vm/logger.go index e0533f2fc2..d5c88bf39a 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -17,11 +17,14 @@ package vm import ( + "bytes" "encoding/hex" "fmt" "io" "math/big" + "runtime" "strings" + "sync" "time" "github.com/holiman/uint256" @@ -67,10 +70,10 @@ type StructLog struct { Op OpCode `json:"op"` Gas uint64 `json:"gas"` GasCost uint64 `json:"gasCost"` - Memory []byte `json:"memory"` + Memory bytes.Buffer `json:"memory"` MemorySize int `json:"memSize"` Stack []uint256.Int `json:"stack"` - ReturnData []byte `json:"returnData"` + ReturnData bytes.Buffer `json:"returnData"` Storage map[common.Hash]common.Hash `json:"-"` Depth int `json:"depth"` RefundCounter uint64 `json:"refund"` @@ -78,6 +81,37 @@ type StructLog struct { Err error `json:"-"` } +var ( + loggerPool = sync.Pool{ + New: func() interface{} { + return &StructLog{ + Stack: make([]uint256.Int, 0), + ExtraData: types.NewExtraData(), + } + }, + } +) + +func NewStructlog(pc uint64, op OpCode, gas, cost uint64, depth int) *StructLog { + + structlog := loggerPool.Get().(*StructLog) + structlog.Pc, structlog.Op, structlog.Gas, structlog.GasCost, structlog.Depth = pc, op, gas, cost, depth + + runtime.SetFinalizer(structlog, func(logger *StructLog) { + logger.clean() + loggerPool.Put(logger) + }) + return structlog +} + +func (s *StructLog) clean() { + s.Memory.Reset() + s.Stack = s.Stack[:0] + s.ReturnData.Reset() + s.Storage = nil + s.ExtraData.Clean() +} + // overrides for gencodec type structLogMarshaling struct { Gas math.HexOrDecimal64 @@ -162,27 +196,24 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop memory := scope.Memory stack := scope.Stack contract := scope.Contract + // create a struct log. + structlog := NewStructlog(pc, op, gas, cost, depth) + // check if already accumulated the specified number of logs if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) { return } // Copy a snapshot of the current memory state to a new buffer - var mem []byte if l.cfg.EnableMemory { - mem = make([]byte, len(memory.Data())) - copy(mem, memory.Data()) + structlog.Memory.Write(memory.Data()) + structlog.MemorySize = memory.Len() } // Copy a snapshot of the current stack state to a new buffer - var stck []uint256.Int if !l.cfg.DisableStack { - stck = make([]uint256.Int, len(stack.Data())) - for i, item := range stack.Data() { - stck[i] = item - } + structlog.Stack = append(structlog.Stack, stack.Data()...) } var ( recordStorageDetail bool - storage Storage storageKey common.Hash storageValue common.Hash ) @@ -197,37 +228,33 @@ func (l *StructLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scop storageValue = stack.data[stack.len()-2].Bytes32() } } - extraData := types.NewExtraData() if recordStorageDetail { contractAddress := contract.Address() if l.storage[contractAddress] == nil { l.storage[contractAddress] = make(Storage) } l.storage[contractAddress][storageKey] = storageValue - storage = l.storage[contractAddress].Copy() + structlog.Storage = l.storage[contractAddress].Copy() - if err := traceStorageProof(l, scope, extraData); err != nil { + if err := traceStorageProof(l, scope, structlog.ExtraData); err != nil { log.Error("Failed to trace data", "opcode", op.String(), "err", err) } } - var rdata []byte if l.cfg.EnableReturnData { - rdata = make([]byte, len(rData)) - copy(rdata, rData) + structlog.ReturnData.Write(rData) } execFuncList, ok := OpcodeExecs[op] if ok { // execute trace func list. for _, exec := range execFuncList { - if err = exec(l, scope, extraData); err != nil { + if err = exec(l, scope, structlog.ExtraData); err != nil { log.Error("Failed to trace data", "opcode", op.String(), "err", err) } } } - // create a new snapshot of the EVM. - structLog := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, l.env.StateDB.GetRefund(), extraData, err} - l.logs = append(l.logs, structLog) + structlog.RefundCounter, structlog.Err = l.env.StateDB.GetRefund(), err + 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) { @@ -309,9 +336,9 @@ func WriteTrace(writer io.Writer, logs []StructLog) { fmt.Fprintf(writer, "%08d %s\n", len(log.Stack)-i-1, log.Stack[i].Hex()) } } - if len(log.Memory) > 0 { + if log.Memory.Len() > 0 { fmt.Fprintln(writer, "Memory:") - fmt.Fprint(writer, hex.Dump(log.Memory)) + fmt.Fprint(writer, hex.Dump(log.Memory.Bytes())) } if len(log.Storage) > 0 { fmt.Fprintln(writer, "Storage:") @@ -319,9 +346,9 @@ func WriteTrace(writer io.Writer, logs []StructLog) { fmt.Fprintf(writer, "%x: %x\n", h, item) } } - if len(log.ReturnData) > 0 { + if log.ReturnData.Len() > 0 { fmt.Fprintln(writer, "ReturnData:") - fmt.Fprint(writer, hex.Dump(log.ReturnData)) + fmt.Fprint(writer, hex.Dump(log.ReturnData.Bytes())) } fmt.Fprintln(writer) } @@ -434,10 +461,10 @@ func FormatLogs(logs []StructLog) []types.StructLogRes { } formatted[index].Stack = &stack } - if len(trace.Memory) != 0 { - memory := make([]string, 0, (len(trace.Memory)+31)/32) - for i := 0; i+32 <= len(trace.Memory); i += 32 { - memory = append(memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + if trace.Memory.Len() != 0 { + memory := make([]string, 0, (trace.Memory.Len()+31)/32) + for i := 0; i+32 <= trace.Memory.Len(); i += 32 { + memory = append(memory, fmt.Sprintf("%x", trace.Memory.Bytes()[i:i+32])) } formatted[index].Memory = &memory } diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go index 881076486b..d2abd22b9d 100644 --- a/core/vm/logger_json.go +++ b/core/vm/logger_json.go @@ -64,13 +64,13 @@ func (l *JSONLogger) CaptureState(pc uint64, op OpCode, gas, cost uint64, scope Err: err, } if l.cfg.EnableMemory { - log.Memory = memory.Data() + log.Memory.Write(memory.Data()) } if !l.cfg.DisableStack { log.Stack = stack.data } if l.cfg.EnableReturnData { - log.ReturnData = rData + log.ReturnData.Write(rData) } l.encoder.Encode(log) }