| // Copyright 2026 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 main | |
| import ( | |
| "encoding/json" | |
| "os" | |
| "strings" | |
| "time" | |
| ) | |
| var ( | |
| debugTrace string | |
| debugTraces []string | |
| distEvents []*traceEvent | |
| ) | |
| // This is a subset of the fields on internal/trace/traceviewer/format.go, | |
| // which we can't use here because it's an internal package and cmd/dist | |
| // needs to be buildable using another Go toolchain. That struct is in turn | |
| // used to represent the Chrome Trace Viewer JSON format whose canonical | |
| // documentation is this Google doc: | |
| // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview | |
| type traceEvent struct { | |
| Name string `json:"name,omitempty"` | |
| Phase string `json:"ph"` | |
| Time float64 `json:"ts"` | |
| PID uint64 `json:"pid"` | |
| TID uint64 `json:"tid"` | |
| } | |
| func maybeTraceFlag(name string) []string { | |
| if debugTrace == "" { | |
| return nil | |
| } | |
| f := pathf("%s/%s.trace", workdir, name) | |
| debugTraces = append(debugTraces, f) | |
| return []string{"-debug-trace=" + f} | |
| } | |
| func timeMicros(t time.Time) float64 { | |
| return float64(t.UnixNano()) / float64(time.Microsecond) | |
| } | |
| type span string | |
| func startSpan(name string) span { | |
| if debugTrace == "" { | |
| return "" | |
| } | |
| distEvents = append(distEvents, &traceEvent{Name: name, Phase: "B", Time: timeMicros(time.Now())}) | |
| return span(name) | |
| } | |
| func (s span) done() { | |
| if s == "" { | |
| return | |
| } | |
| distEvents = append(distEvents, &traceEvent{Name: string(s), Phase: "E", Time: timeMicros(time.Now())}) | |
| } | |
| // writeTrace merges the -debug-trace outputs from the go command invocations and adds | |
| // dist's own trace events in too. Because the traces have absolute timpstamps, we can | |
| // essentially just concatenate them. | |
| func writeTrace() { | |
| f, err := os.Create(debugTrace) | |
| if err != nil { | |
| fatalf("opening trace file for write: %v", err) | |
| } | |
| f.WriteString("[\n") | |
| enc := json.NewEncoder(f) | |
| for _, ev := range distEvents { | |
| if err := enc.Encode(ev); err != nil { | |
| fatalf("marshaling trace event: %v", err) | |
| } | |
| f.WriteString(",") | |
| } | |
| for i, name := range debugTraces { | |
| data, err := os.ReadFile(name) | |
| if err != nil { | |
| fatalf("reading trace file: %v", err) | |
| } | |
| // The trace is a json list: trim the start and end brackets. | |
| events := strings.TrimSpace(string(data)) | |
| events = strings.TrimSuffix(strings.TrimPrefix(events, "["), "]") | |
| f.WriteString(events) | |
| if i != len(debugTraces)-1 { | |
| f.WriteString(",") | |
| } | |
| } | |
| f.WriteString("]\n") | |
| if err := f.Close(); err != nil { | |
| fatalf("closing trace file: %v", err) | |
| } | |
| } |