| // 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) |
| } |
| } |