Merge pull request #2011 from filecoin-project/feat/more-callers

Expose more callers, ellipsis unimportant ones
This commit is contained in:
Łukasz Magiera 2020-06-13 01:55:26 +02:00 committed by GitHub
commit fb4ad043dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 188 additions and 52 deletions

View File

@ -20,35 +20,78 @@ type ExecutionTrace struct {
type GasTrace struct { type GasTrace struct {
Name string Name string
Location string
Location []Loc
TotalGas int64 TotalGas int64
ComputeGas int64 ComputeGas int64
StorageGas int64 StorageGas int64
TimeTaken time.Duration TimeTaken time.Duration
Extra interface{}
Callers []uintptr `json:"-"` Callers []uintptr `json:"-"`
} }
type Loc struct {
File string
Line int
Function string
}
func (l Loc) Show() bool {
ignorePrefix := []string{
"reflect.",
"github.com/filecoin-project/lotus/chain/vm.(*Invoker).transform",
"github.com/filecoin-project/go-amt-ipld/",
}
for _, pre := range ignorePrefix {
if strings.HasPrefix(l.Function, pre) {
return false
}
}
return true
}
func (l Loc) String() string {
file := strings.Split(l.File, "/")
fn := strings.Split(l.Function, "/")
var fnpkg string
if len(fn) > 2 {
fnpkg = strings.Join(fn[len(fn)-2:], "/")
} else {
fnpkg = l.Function
}
return fmt.Sprintf("%s@%s:%d", fnpkg, file[len(file)-1], l.Line)
}
func (l Loc) Important() bool {
if strings.HasPrefix(l.Function, "github.com/filecoin-project/specs-actors/actors/builtin") {
return true
}
return false
}
func (gt *GasTrace) MarshalJSON() ([]byte, error) { func (gt *GasTrace) MarshalJSON() ([]byte, error) {
type GasTraceCopy GasTrace type GasTraceCopy GasTrace
if gt.Location == "" { if len(gt.Location) == 0 {
if len(gt.Callers) != 0 { if len(gt.Callers) != 0 {
frames := runtime.CallersFrames(gt.Callers) frames := runtime.CallersFrames(gt.Callers)
for { for {
frame, more := frames.Next() frame, more := frames.Next()
fn := strings.Split(frame.Function, "/") if frame.Function == "github.com/filecoin-project/lotus/chain/vm.(*VM).ApplyMessage" {
break
split := strings.Split(frame.File, "/") }
file := strings.Join(split[len(split)-2:], "/") l := Loc{
gt.Location += fmt.Sprintf("%s@%s:%d", fn[len(fn)-1], file, frame.Line) File: frame.File,
Line: frame.Line,
Function: frame.Function,
}
gt.Location = append(gt.Location, l)
if !more { if !more {
break break
} }
gt.Location += "|"
} }
} else {
gt.Location = "n/a"
} }
} }

View File

@ -371,6 +371,8 @@ func (rt *Runtime) Send(to address.Address, method abi.MethodNum, m vmr.CBORMars
} }
func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) { func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum, value types.BigInt, params []byte) ([]byte, aerrors.ActorError) {
start := time.Now()
ctx, span := trace.StartSpan(rt.ctx, "vmc.Send") ctx, span := trace.StartSpan(rt.ctx, "vmc.Send")
defer span.End() defer span.End()
if span.IsRecordingEvents() { if span.IsRecordingEvents() {
@ -396,7 +398,7 @@ func (rt *Runtime) internalSend(from, to address.Address, method abi.MethodNum,
} }
defer st.ClearSnapshot() defer st.ClearSnapshot()
ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil) ret, errSend, subrt := rt.vm.send(ctx, msg, rt, nil, start)
if errSend != nil { if errSend != nil {
if errRevert := st.Revert(); errRevert != nil { if errRevert := st.Revert(); errRevert != nil {
return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall") return nil, aerrors.Escalate(errRevert, "failed to revert state tree after failed subcall")
@ -488,22 +490,33 @@ func (rt *Runtime) stateCommit(oldh, newh cid.Cid) aerrors.ActorError {
return nil return nil
} }
func (rt *Runtime) ChargeGas(gas GasCharge) {
err := rt.chargeGasInternal(gas)
if err != nil {
panic(err)
}
}
func (rt *Runtime) finilizeGasTracing() { func (rt *Runtime) finilizeGasTracing() {
if rt.lastGasCharge != nil { if rt.lastGasCharge != nil {
rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime) rt.lastGasCharge.TimeTaken = time.Since(rt.lastGasChargeTime)
} }
} }
func (rt *Runtime) chargeGasInternal(gas GasCharge) aerrors.ActorError { func (rt *Runtime) ChargeGas(gas GasCharge) {
err := rt.chargeGasInternal(gas, 1)
if err != nil {
panic(err)
}
}
func (rt *Runtime) chargeGasFunc(skip int) func(GasCharge) {
return func(gas GasCharge) {
err := rt.chargeGasInternal(gas, 1+skip)
if err != nil {
panic(err)
}
}
}
func (rt *Runtime) chargeGasInternal(gas GasCharge, skip int) aerrors.ActorError {
toUse := gas.Total() toUse := gas.Total()
var callers [3]uintptr var callers [10]uintptr
cout := gruntime.Callers(3, callers[:]) cout := gruntime.Callers(2+skip, callers[:])
now := time.Now() now := time.Now()
if rt.lastGasCharge != nil { if rt.lastGasCharge != nil {
@ -530,7 +543,7 @@ func (rt *Runtime) chargeGasInternal(gas GasCharge) aerrors.ActorError {
} }
func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError { func (rt *Runtime) chargeGasSafe(gas GasCharge) aerrors.ActorError {
return rt.chargeGasInternal(gas) return rt.chargeGasInternal(gas, 1)
} }
func (rt *Runtime) Pricelist() Pricelist { func (rt *Runtime) Pricelist() Pricelist {

View File

@ -103,16 +103,15 @@ func (vm *VM) makeRuntime(ctx context.Context, msg *types.Message, origin addres
allowInternal: true, allowInternal: true,
callerValidated: false, callerValidated: false,
executionTrace: types.ExecutionTrace{Msg: msg}, executionTrace: types.ExecutionTrace{Msg: msg},
lastGasChargeTime: time.Now(),
} }
rt.cst = &cbor.BasicIpldStore{ rt.cst = &cbor.BasicIpldStore{
Blocks: &gasChargingBlocks{rt.ChargeGas, rt.pricelist, vm.cst.Blocks}, Blocks: &gasChargingBlocks{rt.chargeGasFunc(2), rt.pricelist, vm.cst.Blocks},
Atlas: vm.cst.Atlas, Atlas: vm.cst.Atlas,
} }
rt.sys = pricedSyscalls{ rt.sys = pricedSyscalls{
under: vm.Syscalls, under: vm.Syscalls,
chargeGas: rt.ChargeGas, chargeGas: rt.chargeGasFunc(1),
pl: rt.pricelist, pl: rt.pricelist,
} }
@ -172,8 +171,7 @@ type ApplyRet struct {
} }
func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime, func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
gasCharge *GasCharge) ([]byte, aerrors.ActorError, *Runtime) { gasCharge *GasCharge, start time.Time) ([]byte, aerrors.ActorError, *Runtime) {
start := time.Now()
st := vm.cstate st := vm.cstate
@ -189,6 +187,7 @@ func (vm *VM) send(ctx context.Context, msg *types.Message, parent *Runtime,
} }
rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac) rt := vm.makeRuntime(ctx, msg, origin, on, gasUsed, nac)
rt.lastGasChargeTime = start
if parent != nil { if parent != nil {
rt.lastGasChargeTime = parent.lastGasChargeTime rt.lastGasChargeTime = parent.lastGasChargeTime
rt.lastGasCharge = parent.lastGasCharge rt.lastGasCharge = parent.lastGasCharge
@ -273,7 +272,8 @@ func checkMessage(msg *types.Message) error {
func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) { func (vm *VM) ApplyImplicitMessage(ctx context.Context, msg *types.Message) (*ApplyRet, error) {
start := time.Now() start := time.Now()
ret, actorErr, rt := vm.send(ctx, msg, nil, nil) ret, actorErr, rt := vm.send(ctx, msg, nil, nil, start)
rt.finilizeGasTracing()
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
ExitCode: aerrors.RetCode(actorErr), ExitCode: aerrors.RetCode(actorErr),
@ -390,8 +390,7 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
} }
defer st.ClearSnapshot() defer st.ClearSnapshot()
ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas) ret, actorErr, rt := vm.send(ctx, msg, nil, &msgGas, start)
rt.finilizeGasTracing()
if aerrors.IsFatal(actorErr) { if aerrors.IsFatal(actorErr) {
return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr) return nil, xerrors.Errorf("[from=%s,to=%s,n=%d,m=%d,h=%d] fatal error: %w", msg.From, msg.To, msg.Nonce, msg.Method, vm.blockHeight, actorErr)
} }
@ -445,6 +444,8 @@ func (vm *VM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet,
return nil, xerrors.Errorf("gas handling math is wrong") return nil, xerrors.Errorf("gas handling math is wrong")
} }
rt.finilizeGasTracing()
return &ApplyRet{ return &ApplyRet{
MessageReceipt: types.MessageReceipt{ MessageReceipt: types.MessageReceipt{
ExitCode: errcode, ExitCode: errcode,

View File

@ -974,7 +974,39 @@ var compStateTemplate = `
font-size: 12px; font-size: 12px;
border-collapse: collapse; border-collapse: collapse;
} }
tr.sum { border-top: 1px solid black; } tr {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
tr.sum { border-top: 2px solid black; }
tr:first-child { border-top: none; }
tr:last-child { border-bottom: none; }
.ellipsis-content,
.ellipsis-toggle input {
display: none;
}
.ellipsis-toggle {
cursor: pointer;
}
/**
Checked State
**/
.ellipsis-toggle input:checked + .ellipsis {
display: none;
}
.ellipsis-toggle input:checked ~ .ellipsis-content {
display: inline;
background-color: #ddd;
}
hr {
border: none;
height: 1px;
background-color: black;
margin: 0;
}
</style> </style>
</head> </head>
<body> <body>
@ -1023,18 +1055,54 @@ var compStateMsg = `
<div class="error">Error: <pre>{{.Error}}</pre></div> <div class="error">Error: <pre>{{.Error}}</pre></div>
{{end}} {{end}}
<details> <details>
<summary>Gas Trace</summary> <summary>Gas Trace</summary>
<table> <table>
<tr><th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr> <tr><th>Name</th><th>Total/Compute/Storage</th><th>Time Taken</th><th>Location</th></tr>
{{range .GasCharges}} {{range .GasCharges}}
<tr><td>{{.Name}}</td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td><td>{{.Location}}</td></tr> <tr><td>{{.Name}}</td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td>
<td>
{{ $fImp := FirstImportant .Location }}
{{ if $fImp }}
<details>
<summary>{{ $fImp }}</summary><hr />
{{ $elipOn := false }}
{{ range $index, $ele := .Location -}}
{{- if $index }}<br />{{end -}}
{{- if .Show -}}
{{ if $elipOn }}
{{ $elipOn = false }}
</span></label>
{{end}}
{{- if .Important }}<b>{{end -}}
{{- . -}}
{{if .Important }}</b>{{end}}
{{else}}
{{ if not $elipOn }}
{{ $elipOn = true }}
<label class="ellipsis-toggle"><input type="checkbox" /><span class="ellipsis">[]<br /></span>
<span class="ellipsis-content">
{{end}}
{{- "" -}}
{{- . -}}
{{end}}
{{end}}
{{ if $elipOn }}
{{ $elipOn = false }}
</span></label>
{{end}}
</details>
{{end}}
</td></tr>
{{end}} {{end}}
{{with SumGas .GasCharges}} {{with SumGas .GasCharges}}
<tr class="sum"><td><b>Sum</b></td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td><td></td></tr> <tr class="sum"><td><b>Sum</b></td><td>{{.TotalGas}}/{{.ComputeGas}}/{{.StorageGas}}</td><td>{{.TimeTaken}}</td><td></td></tr>
{{end}} {{end}}
</table> </table>
</details> </details>
{{if gt (len .Subcalls) 0}} {{if gt (len .Subcalls) 0}}
<div>Subcalls:</div> <div>Subcalls:</div>
{{$hash := .Hash}} {{$hash := .Hash}}
@ -1062,6 +1130,17 @@ func computeStateHTMLTempl(ts *types.TipSet, o *api.ComputeStateOutput, getCode
"SumGas": sumGas, "SumGas": sumGas,
"CodeStr": codeStr, "CodeStr": codeStr,
"Call": call, "Call": call,
"FirstImportant": func(locs []types.Loc) *types.Loc {
if len(locs) != 0 {
for _, l := range locs {
if l.Important() {
return &l
}
}
return &locs[0]
}
return nil
},
}).Parse(compStateTemplate) }).Parse(compStateTemplate)
if err != nil { if err != nil {
return err return err