eth/tracers/logger: add optional call frames to json logger #29353 (#1459)

Adds a flag `--trace.callframes` to t8n which will log info when entering or exiting a call frame in addition to the execution steps.

---------

Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com>
Co-authored-by: Mario Vega <marioevz@gmail.com>
This commit is contained in:
Daniel Liu 2025-09-13 10:09:36 +08:00 committed by GitHub
parent 6bd8df6b8f
commit 604ba0ea14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 128 additions and 1 deletions

View file

@ -0,0 +1,65 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package logger
import (
"encoding/json"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
)
var _ = (*callFrameMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (c callFrame) MarshalJSON() ([]byte, error) {
type callFrame struct {
From common.Address `json:"from"`
To common.Address `json:"to"`
Input hexutil.Bytes `json:"input,omitempty"`
Gas math.HexOrDecimal64 `json:"gas"`
Value *hexutil.Big `json:"value"`
Type string `json:"type"`
}
var enc callFrame
enc.From = c.From
enc.To = c.To
enc.Input = c.Input
enc.Gas = math.HexOrDecimal64(c.Gas)
enc.Value = (*hexutil.Big)(c.Value)
enc.Type = c.Type()
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (c *callFrame) UnmarshalJSON(input []byte) error {
type callFrame struct {
From *common.Address `json:"from"`
To *common.Address `json:"to"`
Input *hexutil.Bytes `json:"input,omitempty"`
Gas *math.HexOrDecimal64 `json:"gas"`
Value *hexutil.Big `json:"value"`
}
var dec callFrame
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.From != nil {
c.From = *dec.From
}
if dec.To != nil {
c.To = *dec.To
}
if dec.Input != nil {
c.Input = *dec.Input
}
if dec.Gas != nil {
c.Gas = uint64(*dec.Gas)
}
if dec.Value != nil {
c.Value = (*big.Int)(dec.Value)
}
return nil
}

View file

@ -19,14 +19,41 @@ package logger
import (
"encoding/json"
"io"
"math/big"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
"github.com/XinFinOrg/XDPoSChain/common/math"
"github.com/XinFinOrg/XDPoSChain/core/tracing"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/core/vm"
)
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe.go
// overrides for gencodec
type callFrameMarshaling struct {
Input hexutil.Bytes
Gas math.HexOrDecimal64
Value *hexutil.Big
Type string `json:"type"` // adds call to Type() in MarshalJSON
}
// callFrame is emitted every call frame entered.
type callFrame struct {
op vm.OpCode
From common.Address `json:"from"`
To common.Address `json:"to"`
Input []byte `json:"input,omitempty"`
Gas uint64 `json:"gas"`
Value *big.Int `json:"value"`
}
// Type formats the call type in a human-readable format.
func (c *callFrame) Type() string {
return c.op.String()
}
type jsonLogger struct {
encoder *json.Encoder
cfg *Config
@ -48,6 +75,22 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *tracing.Hooks {
}
}
// NewJSONLoggerWithCallFrames creates a new EVM tracer that prints execution steps as JSON objects
// into the provided stream. It also includes call frames in the output.
func NewJSONLoggerWithCallFrames(cfg *Config, writer io.Writer) *tracing.Hooks {
l := &jsonLogger{encoder: json.NewEncoder(writer), cfg: cfg}
if l.cfg == nil {
l.cfg = &Config{}
}
return &tracing.Hooks{
OnTxStart: l.OnTxStart,
OnEnter: l.OnEnter,
OnExit: l.OnExit,
OnOpcode: l.OnOpcode,
OnFault: l.OnFault,
}
}
func (l *jsonLogger) OnFault(pc uint64, op byte, gas uint64, cost uint64, scope tracing.OpContext, depth int, err error) {
// TODO: Add rData to this interface as well
l.OnOpcode(pc, op, gas, cost, scope, nil, depth, err)
@ -79,10 +122,29 @@ func (l *jsonLogger) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracin
l.encoder.Encode(log)
}
func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
// OnEnter is not enabled by default.
func (l *jsonLogger) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
frame := callFrame{
op: vm.OpCode(typ),
From: from,
To: to,
Gas: gas,
Value: value,
}
if l.cfg.EnableMemory {
frame.Input = input
}
l.encoder.Encode(frame)
}
func (l *jsonLogger) OnEnd(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth > 0 {
return
}
l.OnExit(depth, output, gasUsed, err, false)
}
func (l *jsonLogger) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`