17
17
package t8ntool
18
18
19
19
import (
20
- "github.com/ethereum/go-ethereum/common"
21
- "github.com/ethereum/go-ethereum/core/tracing"
22
- "github.com/ethereum/go-ethereum/core/types"
23
- "github.com/ethereum/go-ethereum/eth/tracers"
24
- "github.com/ethereum/go-ethereum/eth/tracers/logger"
25
- "github.com/ethereum/go-ethereum/log"
26
- "github.com/ethereum/go-ethereum/params"
27
-
28
20
"encoding/json"
29
21
"fmt"
30
22
"io"
31
23
"math/big"
32
24
"os"
33
25
"path/filepath"
26
+
27
+ "github.com/ethereum/go-ethereum/common"
28
+ "github.com/ethereum/go-ethereum/core/tracing"
29
+ "github.com/ethereum/go-ethereum/core/types"
30
+ "github.com/ethereum/go-ethereum/eth/tracers"
31
+ "github.com/ethereum/go-ethereum/log"
34
32
)
35
33
36
34
// fileWritingTracer wraps either a tracer or a logger. On tx start,
@@ -41,129 +39,116 @@ type fileWritingTracer struct {
41
39
inner * tracing.Hooks
42
40
destination io.WriteCloser
43
41
baseDir string
44
-
45
- // for json-tracing
46
- logConfig * logger.Config
42
+ // suffix is the suffix to use when creating files
43
+ suffix string
47
44
48
45
// for custom tracing
49
- tracerName string
50
- tracerConf json.RawMessage
51
- chainConfig * params.ChainConfig
52
- getResult func () (json.RawMessage , error )
46
+ getResult func () (json.RawMessage , error )
53
47
}
54
48
55
- // jsonToFile creates hooks which uses an underlying jsonlogger, and writes the
56
- // jsonl-delimited output to a file, one per tx.
57
- func jsonToFile (baseDir string , logConfig * logger.Config , callFrames bool ) * tracing.Hooks {
58
- t := & fileWritingTracer {
59
- baseDir : baseDir ,
60
- logConfig : logConfig ,
49
+ func (l * fileWritingTracer ) Write (p []byte ) (n int , err error ) {
50
+ if l .destination != nil {
51
+ return l .destination .Write (p )
61
52
}
62
- hooks := t . hooks ( )
63
- if ! callFrames {
64
- hooks . OnEnter = nil
65
- }
66
- return hooks
53
+ log . Warn ( "Tracer wrote to non-existing output" )
54
+ // It is tempting to return an error here, however, the json encoder
55
+ // will no retry writing to an io.Writer once it has returned an error once.
56
+ // Therefore, we must squash the error.
57
+ return n , nil
67
58
}
68
59
69
- // tracerToFile creates hooks which uses an underlying tracer, and writes the
70
- // json-result to file, one per tx .
71
- func tracerToFile (baseDir , tracerName string , traceConfig json. RawMessage , chainConfig * params. ChainConfig ) * tracing.Hooks {
60
+ // newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
61
+ // and writes the output to a file, one file per transaction .
62
+ func newFileWriter (baseDir string , innerFn func ( out io. Writer ) * tracing. Hooks ) * tracing.Hooks {
72
63
t := & fileWritingTracer {
73
- baseDir : baseDir ,
74
- tracerName : tracerName ,
75
- chainConfig : chainConfig ,
64
+ baseDir : baseDir ,
65
+ suffix : "jsonl" ,
76
66
}
67
+ t .inner = innerFn (t ) // instantiate the inner tracer
77
68
return t .hooks ()
78
69
}
79
70
80
- func (l * fileWritingTracer ) hooks () * tracing.Hooks {
81
- hooks := & tracing.Hooks {
82
- OnTxStart : l .OnTxStartJSONL ,
83
- OnTxEnd : l .OnTxEnd ,
84
- // intentional no-op: we instantiate the l.inner on tx start, which has
85
- // not yet happened at this point
86
- //OnSystemCallStart: func() {},
87
- OnEnter : func (depth int , typ byte , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
88
- if l .inner != nil && l .inner .OnEnter != nil {
89
- l .inner .OnEnter (depth , typ , from , to , input , gas , value )
90
- }
91
- },
92
- OnExit : func (depth int , output []byte , gasUsed uint64 , err error , reverted bool ) {
93
- if l .inner != nil && l .inner .OnExit != nil {
94
- l .inner .OnExit (depth , output , gasUsed , err , reverted )
95
- }
96
- },
97
- OnOpcode : func (pc uint64 , op byte , gas , cost uint64 , scope tracing.OpContext , rData []byte , depth int , err error ) {
98
- if l .inner .OnOpcode != nil {
99
- l .inner .OnOpcode (pc , op , gas , cost , scope , rData , depth , err )
100
- }
101
- },
102
- OnFault : func (pc uint64 , op byte , gas , cost uint64 , scope tracing.OpContext , depth int , err error ) {
103
- if l .inner != nil && l .inner .OnFault != nil {
104
- l .inner .OnFault (pc , op , gas , cost , scope , depth , err )
105
- }
106
- },
107
- }
108
- if len (l .tracerName ) > 0 { // a custom tracer
109
- hooks .OnTxStart = l .OnTxStartJSON
110
- }
111
- return hooks
112
- }
113
-
114
- // OnTxStartJSONL is the OnTxStart-handler for jsonl logger.
115
- func (l * fileWritingTracer ) OnTxStartJSONL (env * tracing.VMContext , tx * types.Transaction , from common.Address ) {
116
- // Open a new file,
117
- fname := filepath .Join (l .baseDir , fmt .Sprintf ("trace-%d-%v.jsonl" , l .txIndex , tx .Hash ().String ()))
118
- traceFile , err := os .Create (fname )
119
- if err != nil {
120
- log .Warn ("Failed creating trace-file" , "err" , err )
121
- }
122
- log .Debug ("Created tracing-file" , "path" , fname )
123
- l .destination = traceFile
124
- l .inner = logger .NewJSONLoggerWithCallFrames (l .logConfig , traceFile )
125
- if l .inner .OnTxStart != nil {
126
- l .inner .OnTxStart (env , tx , from )
71
+ // newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
72
+ // and writes the result (getResult-output) to file, one per transaction.
73
+ func newResultWriter (baseDir string , tracer * tracers.Tracer ) * tracing.Hooks {
74
+ t := & fileWritingTracer {
75
+ baseDir : baseDir ,
76
+ getResult : tracer .GetResult ,
77
+ inner : tracer .Hooks ,
78
+ suffix : "json" ,
127
79
}
80
+ return t .hooks ()
128
81
}
129
82
130
- // OnTxStartJSONL is the OnTxStart-handler for custom tracer.
131
- func (l * fileWritingTracer ) OnTxStartJSON (env * tracing.VMContext , tx * types.Transaction , from common.Address ) {
83
+ // OnTxStart creates a new output-file specific for this transaction, and invokes
84
+ // the inner OnTxStart handler.
85
+ func (l * fileWritingTracer ) OnTxStart (env * tracing.VMContext , tx * types.Transaction , from common.Address ) {
132
86
// Open a new file,
133
- fname := filepath .Join (l .baseDir , fmt .Sprintf ("trace-%d-%v.json " , l .txIndex , tx .Hash ().String ()))
87
+ fname := filepath .Join (l .baseDir , fmt .Sprintf ("trace-%d-%v.%v " , l .txIndex , tx .Hash ().String (), l . suffix ))
134
88
traceFile , err := os .Create (fname )
135
89
if err != nil {
136
90
log .Warn ("Failed creating trace-file" , "err" , err )
137
91
}
138
- fmt .Printf ("Created tracing-file %v\n " , fname )
139
92
log .Info ("Created tracing-file" , "path" , fname )
140
93
l .destination = traceFile
141
- inner , err := tracers .DefaultDirectory .New (l .tracerName , nil , l .tracerConf , l .chainConfig )
142
- if err != nil {
143
- log .Warn ("Failed instantiating tracer" , "err" , err )
144
- return
145
- }
146
- l .getResult = inner .GetResult
147
- l .inner = inner .Hooks
94
+
148
95
if l .inner .OnTxStart != nil {
149
96
l .inner .OnTxStart (env , tx , from )
150
97
}
151
98
}
152
99
100
+ // OnTxEnd writes result (if getResult exist), closes any currently open output-file,
101
+ // and invokes the inner OnTxEnd handler.
153
102
func (l * fileWritingTracer ) OnTxEnd (receipt * types.Receipt , err error ) {
154
103
if l .inner .OnTxEnd != nil {
155
104
l .inner .OnTxEnd (receipt , err )
156
105
}
157
- if l .getResult != nil {
106
+ if l .getResult != nil && l . destination != nil {
158
107
if result , err := l .getResult (); result != nil {
159
108
json .NewEncoder (l .destination ).Encode (result )
160
109
} else {
161
110
log .Warn ("Error obtaining tracer result" , "err" , err )
162
111
}
163
- }
164
- if l .destination != nil { // Close old file
165
112
l .destination .Close ()
166
113
l .destination = nil
167
114
}
168
115
l .txIndex ++
169
116
}
117
+
118
+ func (l * fileWritingTracer ) hooks () * tracing.Hooks {
119
+ hooks := & tracing.Hooks {
120
+ OnTxStart : l .OnTxStart ,
121
+ OnTxEnd : l .OnTxEnd ,
122
+ OnEnter : func (depth int , typ byte , from common.Address , to common.Address , input []byte , gas uint64 , value * big.Int ) {
123
+ if l .inner != nil && l .inner .OnEnter != nil {
124
+ l .inner .OnEnter (depth , typ , from , to , input , gas , value )
125
+ }
126
+ },
127
+ OnExit : func (depth int , output []byte , gasUsed uint64 , err error , reverted bool ) {
128
+ if l .inner != nil && l .inner .OnExit != nil {
129
+ l .inner .OnExit (depth , output , gasUsed , err , reverted )
130
+ }
131
+ },
132
+ OnOpcode : func (pc uint64 , op byte , gas , cost uint64 , scope tracing.OpContext , rData []byte , depth int , err error ) {
133
+ if l .inner .OnOpcode != nil {
134
+ l .inner .OnOpcode (pc , op , gas , cost , scope , rData , depth , err )
135
+ }
136
+ },
137
+ OnFault : func (pc uint64 , op byte , gas , cost uint64 , scope tracing.OpContext , depth int , err error ) {
138
+ if l .inner != nil && l .inner .OnFault != nil {
139
+ l .inner .OnFault (pc , op , gas , cost , scope , depth , err )
140
+ }
141
+ },
142
+ OnSystemCallStart : func () {
143
+ if l .inner != nil && l .inner .OnSystemCallStart != nil {
144
+ l .inner .OnSystemCallStart ()
145
+ }
146
+ },
147
+ OnSystemCallEnd : func () {
148
+ if l .inner != nil && l .inner .OnSystemCallEnd != nil {
149
+ l .inner .OnSystemCallEnd ()
150
+ }
151
+ },
152
+ }
153
+ return hooks
154
+ }
0 commit comments