mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-03-06 11:25:02 +00:00
collect block level times
This commit is contained in:
parent
c8592a42a8
commit
23bb0c49de
1 changed files with 48 additions and 46 deletions
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue