6
6
"encoding/hex"
7
7
"encoding/json"
8
8
"fmt"
9
+ "io"
10
+ "sort"
9
11
10
12
"github.com/fatih/color"
11
13
"github.com/ipfs/go-cid"
@@ -19,6 +21,7 @@ import (
19
21
"github.com/filecoin-project/lotus/chain/consensus"
20
22
"github.com/filecoin-project/lotus/chain/types"
21
23
lcli "github.com/filecoin-project/lotus/cli"
24
+ "github.com/filecoin-project/lotus/lib/tablewriter"
22
25
)
23
26
24
27
var msgCmd = & cli.Command {
@@ -27,10 +30,19 @@ var msgCmd = &cli.Command{
27
30
Usage : "Translate message between various formats" ,
28
31
ArgsUsage : "Message in any form" ,
29
32
Flags : []cli.Flag {
33
+ & cli.BoolFlag {
34
+ Name : "show-message" ,
35
+ Usage : "Print the message details" ,
36
+ Value : true ,
37
+ },
30
38
& cli.BoolFlag {
31
39
Name : "exec-trace" ,
32
40
Usage : "Print the execution trace" ,
33
41
},
42
+ & cli.BoolFlag {
43
+ Name : "gas-stats" ,
44
+ Usage : "Print a summary of gas charges" ,
45
+ },
34
46
},
35
47
Action : func (cctx * cli.Context ) error {
36
48
if cctx .NArg () != 1 {
@@ -82,16 +94,50 @@ var msgCmd = &cli.Command{
82
94
fmt .Printf ("Return: %x\n " , res .MsgRct .Return )
83
95
fmt .Printf ("Gas Used: %d\n " , res .MsgRct .GasUsed )
84
96
}
97
+
98
+ if cctx .Bool ("gas-stats" ) {
99
+ var printTrace func (descPfx string , trace types.ExecutionTrace )
100
+ printTrace = func (descPfx string , trace types.ExecutionTrace ) {
101
+ typ := "Message"
102
+ if descPfx != "" {
103
+ typ = "Subcall"
104
+ }
105
+ _ , _ = fmt .Fprintln (cctx .App .Writer , color .New (color .Bold ).Sprint (fmt .Sprintf ("%s (%s%s) gas charges:" , typ , descPfx , trace .Msg .To )))
106
+ statsTable (cctx .App .Writer , trace , false )
107
+ for _ , subtrace := range trace .Subcalls {
108
+ _ , _ = fmt .Fprintln (cctx .App .Writer )
109
+ printTrace (descPfx + trace .Msg .To .String ()+ "➜" , subtrace )
110
+ }
111
+ }
112
+ printTrace ("" , res .ExecutionTrace )
113
+ if len (res .ExecutionTrace .Subcalls ) > 0 {
114
+ _ , _ = fmt .Fprintln (cctx .App .Writer )
115
+ _ , _ = fmt .Fprintln (cctx .App .Writer , color .New (color .Bold ).Sprint ("Total gas charges:" ))
116
+ statsTable (cctx .App .Writer , res .ExecutionTrace , true )
117
+ perCallTrace := gasTracesPerCall (res .ExecutionTrace )
118
+ _ , _ = fmt .Fprintln (cctx .App .Writer )
119
+ _ , _ = fmt .Fprintln (cctx .App .Writer , color .New (color .Bold ).Sprint ("Gas charges per message:" ))
120
+ statsTable (cctx .App .Writer , perCallTrace , false )
121
+ }
122
+ }
85
123
}
86
124
87
- switch msg := msg .(type ) {
88
- case * types.SignedMessage :
89
- return printSignedMessage (cctx , msg )
90
- case * types.Message :
91
- return printMessage (cctx , msg )
92
- default :
93
- return xerrors .Errorf ("this error message can't be printed" )
125
+ if cctx .Bool ("show-message" ) {
126
+ switch msg := msg .(type ) {
127
+ case * types.SignedMessage :
128
+ if err := printSignedMessage (cctx , msg ); err != nil {
129
+ return err
130
+ }
131
+ case * types.Message :
132
+ if err := printMessage (cctx , msg ); err != nil {
133
+ return err
134
+ }
135
+ default :
136
+ return xerrors .Errorf ("this error message can't be printed" )
137
+ }
94
138
}
139
+
140
+ return nil
95
141
},
96
142
}
97
143
@@ -335,3 +381,105 @@ func messageFromCID(cctx *cli.Context, c cid.Cid) (types.ChainMsg, error) {
335
381
336
382
return messageFromBytes (cctx , msgb )
337
383
}
384
+
385
+ type gasTally struct {
386
+ storageGas int64
387
+ computeGas int64
388
+ count int
389
+ }
390
+
391
+ func accumGasTallies (charges map [string ]* gasTally , totals * gasTally , trace types.ExecutionTrace , recurse bool ) {
392
+ for _ , charge := range trace .GasCharges {
393
+ name := charge .Name
394
+ if _ , ok := charges [name ]; ! ok {
395
+ charges [name ] = & gasTally {}
396
+ }
397
+ charges [name ].computeGas += charge .ComputeGas
398
+ charges [name ].storageGas += charge .StorageGas
399
+ charges [name ].count ++
400
+ totals .computeGas += charge .ComputeGas
401
+ totals .storageGas += charge .StorageGas
402
+ totals .count ++
403
+ }
404
+ if recurse {
405
+ for _ , subtrace := range trace .Subcalls {
406
+ accumGasTallies (charges , totals , subtrace , recurse )
407
+ }
408
+ }
409
+ }
410
+
411
+ func statsTable (out io.Writer , trace types.ExecutionTrace , recurse bool ) {
412
+ tw := tablewriter .New (
413
+ tablewriter .Col ("Type" ),
414
+ tablewriter .Col ("Count" , tablewriter .RightAlign ()),
415
+ tablewriter .Col ("Storage Gas" , tablewriter .RightAlign ()),
416
+ tablewriter .Col ("S%" , tablewriter .RightAlign ()),
417
+ tablewriter .Col ("Compute Gas" , tablewriter .RightAlign ()),
418
+ tablewriter .Col ("C%" , tablewriter .RightAlign ()),
419
+ tablewriter .Col ("Total Gas" , tablewriter .RightAlign ()),
420
+ tablewriter .Col ("T%" , tablewriter .RightAlign ()),
421
+ )
422
+
423
+ totals := & gasTally {}
424
+ charges := make (map [string ]* gasTally )
425
+ accumGasTallies (charges , totals , trace , recurse )
426
+
427
+ // Sort by name
428
+ names := make ([]string , 0 , len (charges ))
429
+ for name := range charges {
430
+ names = append (names , name )
431
+ }
432
+ sort .Strings (names )
433
+
434
+ for _ , name := range names {
435
+ charge := charges [name ]
436
+ tw .Write (map [string ]interface {}{
437
+ "Type" : name ,
438
+ "Count" : charge .count ,
439
+ "Storage Gas" : charge .storageGas ,
440
+ "S%" : fmt .Sprintf ("%.2f" , float64 (charge .storageGas )/ float64 (totals .storageGas )* 100 ),
441
+ "Compute Gas" : charge .computeGas ,
442
+ "C%" : fmt .Sprintf ("%.2f" , float64 (charge .computeGas )/ float64 (totals .computeGas )* 100 ),
443
+ "Total Gas" : charge .storageGas + charge .computeGas ,
444
+ "T%" : fmt .Sprintf ("%.2f" , float64 (charge .storageGas + charge .computeGas )/ float64 (totals .storageGas + totals .computeGas )* 100 ),
445
+ })
446
+ }
447
+ tw .Write (map [string ]interface {}{
448
+ "Type" : "Total" ,
449
+ "Count" : totals .count ,
450
+ "Storage Gas" : totals .storageGas ,
451
+ "S%" : "100.00" ,
452
+ "Compute Gas" : totals .computeGas ,
453
+ "C%" : "100.00" ,
454
+ "Total Gas" : totals .storageGas + totals .computeGas ,
455
+ "T%" : "100.00" ,
456
+ })
457
+ tw .Flush (out , tablewriter .WithBorders ())
458
+ }
459
+
460
+ // Takes an execution trace and returns a new trace that groups all the gas charges by the message
461
+ // they were charged in, with the gas charges named per message; the output is partial and only
462
+ // suitable for calling statsTable() with.
463
+ func gasTracesPerCall (inTrace types.ExecutionTrace ) types.ExecutionTrace {
464
+ outTrace := types.ExecutionTrace {
465
+ GasCharges : []* types.GasTrace {},
466
+ }
467
+ count := 1
468
+ var accum func (name string , trace types.ExecutionTrace )
469
+ accum = func (name string , trace types.ExecutionTrace ) {
470
+ totals := & gasTally {}
471
+ charges := make (map [string ]* gasTally )
472
+ accumGasTallies (charges , totals , trace , false )
473
+ outTrace .GasCharges = append (outTrace .GasCharges , & types.GasTrace {
474
+ Name : fmt .Sprintf ("#%d %s" , count , name ),
475
+ ComputeGas : totals .computeGas ,
476
+ StorageGas : totals .storageGas ,
477
+ })
478
+ count ++
479
+ for _ , subtrace := range trace .Subcalls {
480
+ accum (name + "➜" + subtrace .Msg .To .String (), subtrace )
481
+ }
482
+ }
483
+ accum (inTrace .Msg .To .String (), inTrace )
484
+ return outTrace
485
+ }
0 commit comments