2019-07-09 17:55:22 +03:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package debug
|
|
|
|
|
|
|
|
import (
|
2019-08-14 19:51:42 +03:00
|
|
|
"context"
|
2019-07-09 17:55:22 +03:00
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"net/http"
|
|
|
|
"sort"
|
2020-03-06 18:28:29 +03:00
|
|
|
"sync"
|
2020-03-27 05:00:12 +03:00
|
|
|
"time"
|
2019-07-09 17:55:22 +03:00
|
|
|
|
2020-04-21 16:41:57 +03:00
|
|
|
"golang.org/x/tools/internal/event"
|
2020-04-17 16:32:56 +03:00
|
|
|
"golang.org/x/tools/internal/event/core"
|
|
|
|
"golang.org/x/tools/internal/event/export"
|
2020-04-20 22:44:34 +03:00
|
|
|
"golang.org/x/tools/internal/event/label"
|
2024-04-16 19:56:26 +03:00
|
|
|
"golang.org/x/tools/internal/jsonrpc2"
|
2019-07-09 17:55:22 +03:00
|
|
|
)
|
|
|
|
|
gopls/test: add type checking for debug server templates
When internal data structures in gopls changed, the templates driving
the debug server failed because fields or methods they relied on
had gone away. At some cost, this change adds tests that all the
fields and methods in the templates can be satisfied.
The test assumes that all the template uses are through render() in
debug/serve.go. It checks that is list of templates is the same
as the server's, and then type checks each template.
The costs are
1. the template type checking is done by a new external dependency,
github.com/jba/templatecheck
2. To avoid adding a new dependency to x/tools, the test is in
the gopls module, not the debug package, so variables from the
debug package have to be exported
In an ideal world the debug package, and much of internal/lsp,
would be in the gopls package. In that case the cost 2 about could
be avoided.
In an ideal world, the core runtime template packages would
contain template checking code, and cost 1 would be unnecessary.
In a more likely (or not so distant) world, perhaps jba would move
(or allow us to move) the template check package into x/tools.
Change-Id: I36b509a00cbdcb5323ee1af3c1193b603c7a907f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/277292
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
2020-12-11 16:34:53 +03:00
|
|
|
var RPCTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(`
|
2019-07-09 17:55:22 +03:00
|
|
|
{{define "title"}}RPC Information{{end}}
|
|
|
|
{{define "body"}}
|
|
|
|
<H2>Inbound</H2>
|
|
|
|
{{template "rpcSection" .Inbound}}
|
|
|
|
<H2>Outbound</H2>
|
|
|
|
{{template "rpcSection" .Outbound}}
|
|
|
|
{{end}}
|
|
|
|
{{define "rpcSection"}}
|
|
|
|
{{range .}}<P>
|
|
|
|
<b>{{.Method}}</b> {{.Started}} <a href="/trace/{{.Method}}">traces</a> ({{.InProgress}} in progress)
|
|
|
|
<br>
|
|
|
|
<i>Latency</i> {{with .Latency}}{{.Mean}} ({{.Min}}<{{.Max}}){{end}}
|
2020-04-03 22:12:15 +03:00
|
|
|
<i>By bucket</i> 0s {{range .Latency.Values}}{{if gt .Count 0}}<b>{{.Count}}</b> {{.Limit}} {{end}}{{end}}
|
2019-07-09 17:55:22 +03:00
|
|
|
<br>
|
2020-04-03 22:12:15 +03:00
|
|
|
<i>Received</i> {{.Received}} (avg. {{.ReceivedMean}})
|
|
|
|
<i>Sent</i> {{.Sent}} (avg. {{.SentMean}})
|
2019-07-09 17:55:22 +03:00
|
|
|
<br>
|
|
|
|
<i>Result codes</i> {{range .Codes}}{{.Key}}={{.Count}} {{end}}
|
|
|
|
</P>
|
|
|
|
{{end}}
|
|
|
|
{{end}}
|
|
|
|
`))
|
|
|
|
|
gopls/test: add type checking for debug server templates
When internal data structures in gopls changed, the templates driving
the debug server failed because fields or methods they relied on
had gone away. At some cost, this change adds tests that all the
fields and methods in the templates can be satisfied.
The test assumes that all the template uses are through render() in
debug/serve.go. It checks that is list of templates is the same
as the server's, and then type checks each template.
The costs are
1. the template type checking is done by a new external dependency,
github.com/jba/templatecheck
2. To avoid adding a new dependency to x/tools, the test is in
the gopls module, not the debug package, so variables from the
debug package have to be exported
In an ideal world the debug package, and much of internal/lsp,
would be in the gopls package. In that case the cost 2 about could
be avoided.
In an ideal world, the core runtime template packages would
contain template checking code, and cost 1 would be unnecessary.
In a more likely (or not so distant) world, perhaps jba would move
(or allow us to move) the template check package into x/tools.
Change-Id: I36b509a00cbdcb5323ee1af3c1193b603c7a907f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/277292
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
2020-12-11 16:34:53 +03:00
|
|
|
type Rpcs struct { // exported for testing
|
2020-03-06 18:28:29 +03:00
|
|
|
mu sync.Mutex
|
2020-04-03 22:12:15 +03:00
|
|
|
Inbound []*rpcStats // stats for incoming lsp rpcs sorted by method name
|
|
|
|
Outbound []*rpcStats // stats for outgoing lsp rpcs sorted by method name
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rpcStats struct {
|
2020-04-03 22:12:15 +03:00
|
|
|
Method string
|
|
|
|
Started int64
|
|
|
|
Completed int64
|
|
|
|
|
|
|
|
Latency rpcTimeHistogram
|
|
|
|
Received byteUnits
|
|
|
|
Sent byteUnits
|
|
|
|
Codes []*rpcCodeBucket
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rpcTimeHistogram struct {
|
|
|
|
Sum timeUnits
|
|
|
|
Count int64
|
|
|
|
Min timeUnits
|
|
|
|
Max timeUnits
|
|
|
|
Values []rpcTimeBucket
|
|
|
|
}
|
|
|
|
|
|
|
|
type rpcTimeBucket struct {
|
|
|
|
Limit timeUnits
|
|
|
|
Count int64
|
|
|
|
}
|
|
|
|
|
|
|
|
type rpcCodeBucket struct {
|
|
|
|
Key string
|
|
|
|
Count int64
|
|
|
|
}
|
|
|
|
|
gopls/test: add type checking for debug server templates
When internal data structures in gopls changed, the templates driving
the debug server failed because fields or methods they relied on
had gone away. At some cost, this change adds tests that all the
fields and methods in the templates can be satisfied.
The test assumes that all the template uses are through render() in
debug/serve.go. It checks that is list of templates is the same
as the server's, and then type checks each template.
The costs are
1. the template type checking is done by a new external dependency,
github.com/jba/templatecheck
2. To avoid adding a new dependency to x/tools, the test is in
the gopls module, not the debug package, so variables from the
debug package have to be exported
In an ideal world the debug package, and much of internal/lsp,
would be in the gopls package. In that case the cost 2 about could
be avoided.
In an ideal world, the core runtime template packages would
contain template checking code, and cost 1 would be unnecessary.
In a more likely (or not so distant) world, perhaps jba would move
(or allow us to move) the template check package into x/tools.
Change-Id: I36b509a00cbdcb5323ee1af3c1193b603c7a907f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/277292
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
2020-12-11 16:34:53 +03:00
|
|
|
func (r *Rpcs) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context {
|
2020-04-03 22:12:15 +03:00
|
|
|
r.mu.Lock()
|
|
|
|
defer r.mu.Unlock()
|
2020-03-27 05:00:12 +03:00
|
|
|
switch {
|
2020-04-21 16:41:57 +03:00
|
|
|
case event.IsStart(ev):
|
2024-02-03 00:54:39 +03:00
|
|
|
if _, stats := r.getRPCSpan(ctx); stats != nil {
|
2020-04-03 22:12:15 +03:00
|
|
|
stats.Started++
|
|
|
|
}
|
2020-04-21 16:41:57 +03:00
|
|
|
case event.IsEnd(ev):
|
2024-02-03 00:54:39 +03:00
|
|
|
span, stats := r.getRPCSpan(ctx)
|
2020-04-03 22:12:15 +03:00
|
|
|
if stats != nil {
|
2024-02-03 00:54:39 +03:00
|
|
|
endRPC(span, stats)
|
2020-03-27 05:00:12 +03:00
|
|
|
}
|
2020-04-21 16:41:57 +03:00
|
|
|
case event.IsMetric(ev):
|
2024-04-16 19:56:26 +03:00
|
|
|
sent := byteUnits(jsonrpc2.SentBytes.Get(lm))
|
|
|
|
rec := byteUnits(jsonrpc2.ReceivedBytes.Get(lm))
|
2020-04-03 22:12:15 +03:00
|
|
|
if sent != 0 || rec != 0 {
|
2024-02-03 00:54:39 +03:00
|
|
|
if _, stats := r.getRPCSpan(ctx); stats != nil {
|
2020-04-03 22:12:15 +03:00
|
|
|
stats.Sent += sent
|
|
|
|
stats.Received += rec
|
2020-03-27 05:00:12 +03:00
|
|
|
}
|
|
|
|
}
|
2020-03-17 23:00:16 +03:00
|
|
|
}
|
2020-04-03 22:12:15 +03:00
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2024-02-03 00:54:39 +03:00
|
|
|
func endRPC(span *export.Span, stats *rpcStats) {
|
2020-04-03 22:12:15 +03:00
|
|
|
// update the basic counts
|
|
|
|
stats.Completed++
|
|
|
|
|
|
|
|
// get and record the status code
|
|
|
|
if status := getStatusCode(span); status != "" {
|
|
|
|
var b *rpcCodeBucket
|
|
|
|
for c, entry := range stats.Codes {
|
|
|
|
if entry.Key == status {
|
|
|
|
b = stats.Codes[c]
|
|
|
|
break
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
2020-04-03 22:12:15 +03:00
|
|
|
}
|
|
|
|
if b == nil {
|
|
|
|
b = &rpcCodeBucket{Key: status}
|
|
|
|
stats.Codes = append(stats.Codes, b)
|
|
|
|
sort.Slice(stats.Codes, func(i int, j int) bool {
|
|
|
|
return stats.Codes[i].Key < stats.Codes[j].Key
|
2020-03-17 23:00:16 +03:00
|
|
|
})
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
2020-04-03 22:12:15 +03:00
|
|
|
b.Count++
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
|
|
|
|
2020-04-03 22:12:15 +03:00
|
|
|
// calculate latency if this was an rpc span
|
2020-04-21 06:00:05 +03:00
|
|
|
elapsedTime := span.Finish().At().Sub(span.Start().At())
|
2020-04-03 22:12:15 +03:00
|
|
|
latencyMillis := timeUnits(elapsedTime) / timeUnits(time.Millisecond)
|
|
|
|
if stats.Latency.Count == 0 {
|
|
|
|
stats.Latency.Min = latencyMillis
|
|
|
|
stats.Latency.Max = latencyMillis
|
|
|
|
} else {
|
|
|
|
if stats.Latency.Min > latencyMillis {
|
|
|
|
stats.Latency.Min = latencyMillis
|
|
|
|
}
|
|
|
|
if stats.Latency.Max < latencyMillis {
|
|
|
|
stats.Latency.Max = latencyMillis
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-03 22:12:15 +03:00
|
|
|
stats.Latency.Count++
|
|
|
|
stats.Latency.Sum += latencyMillis
|
|
|
|
for i := range stats.Latency.Values {
|
|
|
|
if stats.Latency.Values[i].Limit > latencyMillis {
|
|
|
|
stats.Latency.Values[i].Count++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-03 00:54:39 +03:00
|
|
|
func (r *Rpcs) getRPCSpan(ctx context.Context) (*export.Span, *rpcStats) {
|
2020-04-03 22:12:15 +03:00
|
|
|
// get the span
|
|
|
|
span := export.GetSpan(ctx)
|
|
|
|
if span == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
// use the span start event look up the correct stats block
|
|
|
|
// we do this because it prevents us matching a sub span
|
2020-04-04 05:59:59 +03:00
|
|
|
return span, r.getRPCStats(span.Start())
|
2020-04-03 22:12:15 +03:00
|
|
|
}
|
|
|
|
|
gopls/test: add type checking for debug server templates
When internal data structures in gopls changed, the templates driving
the debug server failed because fields or methods they relied on
had gone away. At some cost, this change adds tests that all the
fields and methods in the templates can be satisfied.
The test assumes that all the template uses are through render() in
debug/serve.go. It checks that is list of templates is the same
as the server's, and then type checks each template.
The costs are
1. the template type checking is done by a new external dependency,
github.com/jba/templatecheck
2. To avoid adding a new dependency to x/tools, the test is in
the gopls module, not the debug package, so variables from the
debug package have to be exported
In an ideal world the debug package, and much of internal/lsp,
would be in the gopls package. In that case the cost 2 about could
be avoided.
In an ideal world, the core runtime template packages would
contain template checking code, and cost 1 would be unnecessary.
In a more likely (or not so distant) world, perhaps jba would move
(or allow us to move) the template check package into x/tools.
Change-Id: I36b509a00cbdcb5323ee1af3c1193b603c7a907f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/277292
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
2020-12-11 16:34:53 +03:00
|
|
|
func (r *Rpcs) getRPCStats(lm label.Map) *rpcStats {
|
2024-04-16 19:56:26 +03:00
|
|
|
method := jsonrpc2.Method.Get(lm)
|
2020-04-03 22:12:15 +03:00
|
|
|
if method == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
set := &r.Inbound
|
2024-04-16 19:56:26 +03:00
|
|
|
if jsonrpc2.RPCDirection.Get(lm) != jsonrpc2.Inbound {
|
2020-04-03 22:12:15 +03:00
|
|
|
set = &r.Outbound
|
|
|
|
}
|
|
|
|
// get the record for this method
|
|
|
|
index := sort.Search(len(*set), func(i int) bool {
|
|
|
|
return (*set)[i].Method >= method
|
|
|
|
})
|
|
|
|
|
|
|
|
if index < len(*set) && (*set)[index].Method == method {
|
|
|
|
return (*set)[index]
|
|
|
|
}
|
|
|
|
|
|
|
|
old := *set
|
|
|
|
*set = make([]*rpcStats, len(old)+1)
|
|
|
|
copy(*set, old[:index])
|
|
|
|
copy((*set)[index+1:], old[index:])
|
|
|
|
stats := &rpcStats{Method: method}
|
|
|
|
stats.Latency.Values = make([]rpcTimeBucket, len(millisecondsDistribution))
|
|
|
|
for i, m := range millisecondsDistribution {
|
|
|
|
stats.Latency.Values[i].Limit = timeUnits(m)
|
|
|
|
}
|
|
|
|
(*set)[index] = stats
|
|
|
|
return stats
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *rpcStats) InProgress() int64 { return s.Started - s.Completed }
|
|
|
|
func (s *rpcStats) SentMean() byteUnits { return s.Sent / byteUnits(s.Started) }
|
|
|
|
func (s *rpcStats) ReceivedMean() byteUnits { return s.Received / byteUnits(s.Started) }
|
|
|
|
|
|
|
|
func (h *rpcTimeHistogram) Mean() timeUnits { return h.Sum / timeUnits(h.Count) }
|
|
|
|
|
|
|
|
func getStatusCode(span *export.Span) string {
|
|
|
|
for _, ev := range span.Events() {
|
2024-04-16 19:56:26 +03:00
|
|
|
if status := jsonrpc2.StatusCode.Get(ev); status != "" {
|
2020-04-03 22:12:15 +03:00
|
|
|
return status
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
2019-07-09 17:55:22 +03:00
|
|
|
}
|
|
|
|
|
gopls/test: add type checking for debug server templates
When internal data structures in gopls changed, the templates driving
the debug server failed because fields or methods they relied on
had gone away. At some cost, this change adds tests that all the
fields and methods in the templates can be satisfied.
The test assumes that all the template uses are through render() in
debug/serve.go. It checks that is list of templates is the same
as the server's, and then type checks each template.
The costs are
1. the template type checking is done by a new external dependency,
github.com/jba/templatecheck
2. To avoid adding a new dependency to x/tools, the test is in
the gopls module, not the debug package, so variables from the
debug package have to be exported
In an ideal world the debug package, and much of internal/lsp,
would be in the gopls package. In that case the cost 2 about could
be avoided.
In an ideal world, the core runtime template packages would
contain template checking code, and cost 1 would be unnecessary.
In a more likely (or not so distant) world, perhaps jba would move
(or allow us to move) the template check package into x/tools.
Change-Id: I36b509a00cbdcb5323ee1af3c1193b603c7a907f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/277292
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Trust: Peter Weinberger <pjw@google.com>
2020-12-11 16:34:53 +03:00
|
|
|
func (r *Rpcs) getData(req *http.Request) interface{} {
|
2019-07-09 17:55:22 +03:00
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func units(v float64, suffixes []string) string {
|
|
|
|
s := ""
|
|
|
|
for _, s = range suffixes {
|
|
|
|
n := v / 1000
|
|
|
|
if n < 1 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
v = n
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%.2f%s", v, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
type timeUnits float64
|
|
|
|
|
|
|
|
func (v timeUnits) String() string {
|
|
|
|
v = v * 1000 * 1000
|
|
|
|
return units(float64(v), []string{"ns", "μs", "ms", "s"})
|
|
|
|
}
|
|
|
|
|
|
|
|
type byteUnits float64
|
|
|
|
|
|
|
|
func (v byteUnits) String() string {
|
|
|
|
return units(float64(v), []string{"B", "KB", "MB", "GB", "TB"})
|
|
|
|
}
|