Source file src/cmd/dist/debugtrace.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/json"
     9  	"os"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  var (
    15  	debugTrace  string
    16  	debugTraces []string
    17  	distEvents  []*traceEvent
    18  )
    19  
    20  // This is a subset of the fields on internal/trace/traceviewer/format.go,
    21  // which we can't use here because it's an internal package and cmd/dist
    22  // needs to be buildable using another Go toolchain. That struct is in turn
    23  // used to represent the Chrome Trace Viewer JSON format whose canonical
    24  // documentation is this Google doc:
    25  // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
    26  type traceEvent struct {
    27  	Name  string  `json:"name,omitempty"`
    28  	Phase string  `json:"ph"`
    29  	Time  float64 `json:"ts"`
    30  	PID   uint64  `json:"pid"`
    31  	TID   uint64  `json:"tid"`
    32  }
    33  
    34  func maybeTraceFlag(name string) []string {
    35  	if debugTrace == "" {
    36  		return nil
    37  	}
    38  	f := pathf("%s/%s.trace", workdir, name)
    39  	debugTraces = append(debugTraces, f)
    40  	return []string{"-debug-trace=" + f}
    41  }
    42  
    43  func timeMicros(t time.Time) float64 {
    44  	return float64(t.UnixNano()) / float64(time.Microsecond)
    45  }
    46  
    47  type span string
    48  
    49  func startSpan(name string) span {
    50  	if debugTrace == "" {
    51  		return ""
    52  	}
    53  	distEvents = append(distEvents, &traceEvent{Name: name, Phase: "B", Time: timeMicros(time.Now())})
    54  	return span(name)
    55  }
    56  
    57  func (s span) done() {
    58  	if s == "" {
    59  		return
    60  	}
    61  	distEvents = append(distEvents, &traceEvent{Name: string(s), Phase: "E", Time: timeMicros(time.Now())})
    62  }
    63  
    64  // writeTrace merges the -debug-trace outputs from the go command invocations and adds
    65  // dist's own trace events in too. Because the traces have absolute timpstamps, we can
    66  // essentially just concatenate them.
    67  func writeTrace() {
    68  	f, err := os.Create(debugTrace)
    69  	if err != nil {
    70  		fatalf("opening trace file for write: %v", err)
    71  	}
    72  	f.WriteString("[\n")
    73  
    74  	enc := json.NewEncoder(f)
    75  	for _, ev := range distEvents {
    76  		if err := enc.Encode(ev); err != nil {
    77  			fatalf("marshaling trace event: %v", err)
    78  		}
    79  		f.WriteString(",")
    80  	}
    81  
    82  	for i, name := range debugTraces {
    83  		data, err := os.ReadFile(name)
    84  		if err != nil {
    85  			fatalf("reading trace file: %v", err)
    86  		}
    87  		// The trace is a json list: trim the start and end brackets.
    88  		events := strings.TrimSpace(string(data))
    89  		events = strings.TrimSuffix(strings.TrimPrefix(events, "["), "]")
    90  		f.WriteString(events)
    91  		if i != len(debugTraces)-1 {
    92  			f.WriteString(",")
    93  		}
    94  	}
    95  
    96  	f.WriteString("]\n")
    97  	if err := f.Close(); err != nil {
    98  		fatalf("closing trace file: %v", err)
    99  	}
   100  }
   101  

View as plain text