collect block level times

This commit is contained in:
Sina Mahmoodi 2025-04-25 15:40:44 +02:00
parent c8592a42a8
commit 23bb0c49de

View file

@ -17,7 +17,6 @@
package live
import (
"encoding/csv"
"encoding/json"
"errors"
"fmt"
@ -36,20 +35,21 @@ func init() {
}
type perfTracerConfig struct {
CSVPath string `json:"csvPath"`
Path string `json:"path"`
}
// perfTracer is a live tracer that measures and records transaction processing performance metrics.
// It tracks total processing time, IO time (account and storage reads), and EVM execution time for
// each transaction. The metrics are written to a CSV file.
// each transaction. The metrics are written to a JSONL file.
type perfTracer struct {
csvPath string
writer *csv.Writer
path string
file *os.File
encoder *json.Encoder
// Block context
currentBlock *types.Block
currentBlockHash common.Hash
blockStartTime time.Time
// Transaction tracking
txStartTime time.Time
@ -59,6 +59,9 @@ type perfTracer struct {
prevAccountReads time.Duration
prevStorageReads time.Duration
// Transaction data collection
txData []map[string]interface{}
statedb tracing.StateDB
}
@ -67,36 +70,20 @@ func newPerfTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, fmt.Errorf("failed to parse config: %v", err)
}
if config.CSVPath == "" {
return nil, errors.New("csv path is required")
if config.Path == "" {
return nil, errors.New("path is required")
}
// Open CSV file
file, err := os.OpenFile(config.CSVPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// Open JSONL file
file, err := os.OpenFile(config.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return nil, fmt.Errorf("failed to open CSV file: %v", err)
}
writer := csv.NewWriter(file)
// Write header if file is empty
info, err := file.Stat()
if err != nil {
file.Close()
return nil, fmt.Errorf("failed to get file info: %v", err)
}
if info.Size() == 0 {
header := []string{"block_number", "block_hash", "tx_index", "tx_hash", "total_time_ns", "io_time_ns", "evm_time_ns", "gas_used"}
if err := writer.Write(header); err != nil {
file.Close()
return nil, fmt.Errorf("failed to write CSV header: %v", err)
}
writer.Flush()
return nil, fmt.Errorf("failed to open JSONL file: %v", err)
}
t := &perfTracer{
csvPath: config.CSVPath,
writer: writer,
path: config.Path,
file: file,
encoder: json.NewEncoder(file),
}
return &tracing.Hooks{
@ -112,6 +99,8 @@ func (t *perfTracer) OnBlockStart(event tracing.BlockEvent) {
t.currentBlock = event.Block
t.currentBlockHash = event.Block.Hash()
t.txIndex = 0
t.txData = make([]map[string]interface{}, 0)
t.blockStartTime = time.Now()
// Reset previous IO measurements for the new block
t.prevAccountReads = 0
t.prevStorageReads = 0
@ -143,36 +132,49 @@ func (t *perfTracer) OnTxEnd(receipt *types.Receipt, err error) {
evmTime = totalTime - ioTime
}
row := []string{
t.currentBlock.Number().String(),
t.currentBlockHash.Hex(),
fmt.Sprintf("%d", t.txIndex),
receipt.TxHash.Hex(),
fmt.Sprintf("%d", totalTime.Nanoseconds()),
fmt.Sprintf("%d", ioTime.Nanoseconds()),
fmt.Sprintf("%d", evmTime.Nanoseconds()),
fmt.Sprintf("%d", receipt.GasUsed),
}
if err := t.writer.Write(row); err != nil {
fmt.Printf("Failed to write CSV row: %v\n", err)
txRecord := map[string]interface{}{
"txIndex": fmt.Sprintf("0x%x", t.txIndex),
"txHash": receipt.TxHash.Hex(),
"gasUsed": fmt.Sprintf("0x%x", receipt.GasUsed),
"totalTime": fmt.Sprintf("0x%x", totalTime.Nanoseconds()),
"ioTime": fmt.Sprintf("0x%x", ioTime.Nanoseconds()),
"evmTime": fmt.Sprintf("0x%x", evmTime.Nanoseconds()),
}
t.txData = append(t.txData, txRecord)
t.prevAccountReads = accumulatedIO.AccountReads
t.prevStorageReads = accumulatedIO.StorageReads
t.txIndex++
}
// OnBlockEnd implements tracing.BlockEndHook
func (t *perfTracer) OnBlockEnd(err error) {
if t.writer != nil {
t.writer.Flush()
// Calculate block-level timings
totalTime := time.Since(t.blockStartTime)
blockEndIO := t.statedb.GetAccumulatedIOMeasurements()
ioTime := blockEndIO.AccountReads + blockEndIO.StorageReads
evmTime := totalTime - ioTime
blockRecord := map[string]interface{}{
"blockNumber": fmt.Sprintf("0x%x", t.currentBlock.Number()),
"blockHash": t.currentBlockHash.Hex(),
"gasUsed": fmt.Sprintf("0x%x", t.currentBlock.GasUsed()),
"totalTime": fmt.Sprintf("0x%x", totalTime.Nanoseconds()),
"ioTime": fmt.Sprintf("0x%x", ioTime.Nanoseconds()),
"evmTime": fmt.Sprintf("0x%x", evmTime.Nanoseconds()),
"transactions": t.txData,
}
if err := t.encoder.Encode(blockRecord); err != nil {
fmt.Printf("Failed to write block record: %v\n", err)
}
if t.file != nil {
t.file.Sync()
}
}
func (t *perfTracer) OnClose() {
if t.writer != nil {
t.writer.Flush()
}
if t.file != nil {
t.file.Close()
}