Merge branch 'master' into vtrace
diff --git a/services/debug/debug/browse.go b/services/debug/debug/browse.go
index bf0c535..2826698 100644
--- a/services/debug/debug/browse.go
+++ b/services/debug/debug/browse.go
@@ -15,13 +15,16 @@
"os/exec"
"runtime"
"strings"
+ "time"
"v.io/v23"
+ "v.io/v23/uniqueid"
"v.io/v23/context"
"v.io/v23/naming"
"v.io/v23/security"
"v.io/v23/services/logreader"
"v.io/v23/services/stats"
+ svtrace "v.io/v23/services/vtrace"
"v.io/v23/vdl"
"v.io/v23/verror"
"v.io/v23/vtrace"
@@ -29,6 +32,7 @@
"v.io/x/ref/lib/signals"
"v.io/x/ref/lib/v23cmd"
"v.io/x/ref/services/internal/pproflib"
+ "encoding/hex"
)
func init() {
@@ -99,6 +103,8 @@
http.Handle("/glob", &globHandler{ctx})
http.Handle(browseProfilesPath, &profilesHandler{ctx})
http.Handle(browseProfilesPath+"/", &profilesHandler{ctx})
+ http.Handle("/vtraces", &allTracesHandler{ctx})
+ http.Handle("/vtrace", &vtraceHandler{ctx})
http.Handle("/favicon.ico", http.NotFoundHandler())
addr := flagBrowseAddr
if len(addr) == 0 {
@@ -477,6 +483,230 @@
pproflib.PprofProxy(h.ctx, browseProfilesPath, name).ServeHTTP(w, r)
}
+type allTracesHandler struct { ctx *context.T }
+
+func (a *allTracesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var (
+ server = r.FormValue("n")
+ name = naming.Join(server, "__debug", "vtrace")
+ )
+ if len(server) == 0 {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "Must specify a server with the URL query parameter 'n'")
+ return
+ }
+ ctx, tracer := newTracer(a.ctx)
+ stub := svtrace.StoreClient(name)
+
+ fmt.Println("calling out to", name)
+ call, err := stub.AllTraces(ctx)
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprintf(w, "Failed to get all trace ids on %s: %v", name, err)
+ return
+ }
+ stream := call.RecvStream()
+ var traces []string
+ for stream.Advance() {
+ id := stream.Value().Id
+ fmt.Printf("Got back: %x", id)
+ traces = append(traces, hex.EncodeToString(id[:]))
+ }
+
+ data := struct {
+ Ids []string
+ StreamError error
+ CallError error
+ ServerName string
+ CommandLine string
+ Vtrace *Tracer
+ }{
+ Ids: traces,
+ StreamError: stream.Err(),
+ CallError: call.Finish(),
+ CommandLine: fmt.Sprintf("debug vtraces %q", name),
+ ServerName: server,
+ Vtrace: tracer,
+ }
+ executeTemplate(a.ctx, w, r, tmplBrowseAllTraces, data)
+ fmt.Println("Done with template")
+}
+
+type divTree struct {
+ // Start is a value from 0-100 which is the percentage of time into the parent span's duration
+ // that this span started.
+ Start int
+ // End is a value from 0-100 which is the percentage of time into the parent span's duration
+ // that this span ended.
+ Width int
+ // VerticalOffset is the number of pixels from the top of the parent div this div should appear.
+ VerticalOffset int
+ Name string
+ Annotations []annotation
+ Children []divTree
+}
+
+type annotation struct {
+ // Position is a value from 0-100 which is the percentage of time into the span's duration that this
+ // annotation occured.
+ Position int
+ Msg string
+}
+
+func convertToTree(n *vtrace.Node, parentStart time.Time, parentEnd time.Time, verticalPosition int) *divTree {
+ startTime := n.Span.Start
+ if startTime.IsZero() {
+ startTime = parentStart
+ }
+
+ endTime := n.Span.End
+ if endTime.IsZero() {
+ endTime = parentEnd
+ }
+
+ parentDuration := parentEnd.Sub(parentStart).Seconds()
+ fmt.Println("parent start is", parentStart, "start", startTime, "parentEnd", parentEnd, "end", endTime)
+ start := int(100 * startTime.Sub(parentStart).Seconds() / parentDuration)
+ width := int(100 * endTime.Sub(parentStart).Seconds() / parentDuration) - start
+ fmt.Println("start:", start, "end", width)
+ top := &divTree{
+ Start: start,
+ Width: width,
+ Name: n.Span.Name,
+ VerticalOffset: verticalPosition,
+ }
+ top.Annotations = make([]annotation, len(n.Span.Annotations))
+ for i, a := range n.Span.Annotations {
+ top.Annotations[i].Msg = a.Message
+ if a.When.IsZero() {
+ top.Annotations[i].Position = 0
+ continue
+ }
+ top.Annotations[i].Position = int(100 * a.When.Sub(parentStart).Seconds() / parentDuration) - start
+ }
+ top.Children = make([]divTree, len(n.Children))
+ pos := 2
+ for i, c := range n.Children {
+ top.Children[i] = *convertToTree(c, startTime, endTime, pos)
+ pos += 2
+ }
+ return top
+
+}
+
+func findStartTime(n *vtrace.Node) time.Time {
+ if !n.Span.Start.IsZero() {
+ return n.Span.Start
+ }
+ var startTime time.Time
+ for _, a := range n.Span.Annotations {
+ startTime = a.When
+ if !startTime.IsZero() {
+ break
+ }
+ }
+ for _, c := range n.Children {
+ childStartTime := findStartTime(c)
+ if startTime.IsZero() || (!childStartTime.IsZero() && startTime.After(childStartTime)) {
+ startTime = childStartTime
+ }
+ if !startTime.IsZero() {
+ break;
+ }
+ }
+ return startTime
+}
+
+func findEndTime(n *vtrace.Node) time.Time {
+ if !n.Span.End.IsZero() {
+ return n.Span.End
+ }
+
+ size := len(n.Span.Annotations)
+ var endTime time.Time
+ for i := range n.Span.Annotations {
+ endTime = n.Span.Annotations[size - 1 - i].When
+ if !endTime.IsZero() {
+ break
+ }
+ }
+
+ size = len(n.Children)
+ for i := range n.Children {
+ childEndTime := findEndTime(n.Children[size - 1 - i])
+ if endTime.IsZero() || (!childEndTime.IsZero() && childEndTime.After(endTime)) {
+ endTime = childEndTime
+ }
+ if !endTime.IsZero() {
+ break
+ }
+ }
+ return endTime
+}
+
+type vtraceHandler struct {ctx *context.T}
+
+func (v *vtraceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var (
+ server = r.FormValue("n")
+ traceId = r.FormValue("t")
+ name = naming.Join(server, "__debug", "vtrace")
+ )
+ if len(server) == 0 {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "Must specify a server with the URL query parameter 'n'")
+ return
+ }
+
+ stub := svtrace.StoreClient(name)
+ ctx, tracer := newTracer(v.ctx)
+ id, err := uniqueid.FromHexString(traceId)
+
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "Invalid trace id %s: %v", traceId, err)
+ return
+ }
+
+ trace, err := stub.Trace(ctx, id)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ fmt.Fprintf(w, "Unknown trace id: %s", traceId)
+ return
+ }
+
+ var buf bytes.Buffer
+
+ vtrace.FormatTrace(&buf, &trace, nil)
+ node := vtrace.BuildTree(&trace)
+
+ if node == nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ fmt.Fprintf(w, "coud not find root span for trace")
+ return
+ }
+
+ tree := convertToTree(node, findStartTime(node), findEndTime(node), 0)
+ data := struct{
+ Id string
+ Root *divTree
+ ServerName string
+ CommandLine string
+ DebugTrace string
+ Vtrace *Tracer
+ }{
+ Id: traceId,
+ Root: tree,
+ ServerName: server,
+ CommandLine: fmt.Sprintf("debug vtraces %q", name),
+ Vtrace: tracer,
+ DebugTrace: buf.String(),
+
+ }
+ executeTemplate(v.ctx, w, r, tmplBrowseVtrace, data)
+}
+
+
func writeEvent(w http.ResponseWriter, data string) {
fmt.Fprintf(w, "data: %s\n\n", strings.TrimSpace(data))
}
@@ -490,6 +720,8 @@
content = "{{template `.header` .}}" + content + "{{template `.footer` .}}"
t := template.Must(tmplBrowseHeader.Clone())
t = template.Must(t.AddParseTree(tmplBrowseFooter.Name(), tmplBrowseFooter.Tree))
+ colors := []string{"red", "blue", "green"}
+ pos := 0
t = t.Funcs(template.FuncMap{
"verrorID": verror.ErrorID,
"unmarshalPublicKey": security.UnmarshalPublicKey,
@@ -505,6 +737,11 @@
vdl.Convert(&ret, v)
return ret
},
+ "nextColor": func() string {
+ c := colors[pos]
+ pos = (pos + 1) % len(colors)
+ return c
+ },
})
t = template.Must(t.New(name).Parse(content))
return t
@@ -781,6 +1018,34 @@
</div>
</section>
`)
+ tmplBrowseAllTraces = makeTemplate("alltraces", `
+<section class="section-center mdl-grid">
+ <ul>
+ {{$name := .ServerName}}
+ {{range .Ids}}
+ <li><a href="/vtrace?n={{$name}}&t={{.}}">{{.}}</a></li>
+ {{end}}
+ </ul>
+ </section>
+`)
+ tmplBrowseVtrace = makeTemplate("vtrace",`
+{{define ".span"}}
+<div style="position:relative;left:{{.Start}}%;width:{{.Width}}%;top:{{.VerticalOffset}}px;margin:0px;padding:0px">
+ <!-- Root span -->
+ <div title="{{.Name}}" style="position:relative;left:0%;width:100%;background:{{nextColor}};height:15px;display:block;margin:0px;padding:0px"></div>
+ {{range $i, $child := .Children}}
+ {{template ".span" $child}}
+ {{end}}
+</div>
+{{end}}
+<section class="section--center mdl-grid">
+ <h5>Vtrace for {{.Id}}</h5>
+ <pre>{{.DebugTrace}}</pre>
+ <div id="parent" class="mdl-cell mdl-cell--12-col">
+ {{template ".span" .Root}}
+ </div>
+</section>
+`)
tmplBrowseHeader = template.Must(template.New(".header").Parse(`
{{define ".header"}}
@@ -813,6 +1078,7 @@
<a class="mdl-navigation__link" href="/logs?n={{.ServerName}}">Logs</a>
<a class="mdl-navigation__link" href="/glob?n={{.ServerName}}">Glob</a>
<a class="mdl-navigation__link" href="/profiles?n={{.ServerName}}">Profiles</a>
+ <a class="mdl-navigation__link" href="/vtraces?n={{.ServerName}}">Traces</a>
</nav>
</div>
</header>
@@ -824,6 +1090,7 @@
<a class="mdl-navigation__link" href="/logs?n={{.ServerName}}">Logs</a>
<a class="mdl-navigation__link" href="/glob?n={{.ServerName}}">Glob</a>
<a class="mdl-navigation__link" href="/profiles?n={{.ServerName}}">Profiles</a>
+ <a class="mdl-navigation__link" href="/vtraces?n={{.ServerName}}">Traces</a>
</nav>
</div>
<main class="mdl-layout__content">