Skip to content

Commit

Permalink
debug: add trace flight recorder
Browse files Browse the repository at this point in the history
Use golang.org/x/exp/trace to implement an trace recorder that saves the trace
to a circular buffer and can be retrieved at any time.

Debug endpoints have been added under /debug/flight to start and stop the trace
as well as to set its period.

Due to golang.org/x/exp/trace, the minimum go version has been bumped to 1.22

Signed-off-by: Alberto Garcia Hierro <[email protected]>
  • Loading branch information
fiam committed Sep 23, 2024
1 parent 83bc8df commit 902ba23
Show file tree
Hide file tree
Showing 30 changed files with 7,617 additions and 19 deletions.
2 changes: 2 additions & 0 deletions cmd/buildkitd/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ func setupDebugHandlers(addr string) error {

m.Handle("/metrics", promhttp.Handler())

setupDebugFlight(m)

// setting debugaddr is opt-in. permission is defined by listener address
trace.AuthRequest = func(_ *http.Request) (bool, bool) {
return true, true
Expand Down
89 changes: 89 additions & 0 deletions cmd/buildkitd/debug_flight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package main

import (
"fmt"
"net/http"
"sync"
"time"

"golang.org/x/exp/trace"
)

type flightRecorder struct {
mu sync.Mutex
cond *sync.Cond
recorder *trace.FlightRecorder
}

func newFlightRecorder() *flightRecorder {
dbg := &flightRecorder{
recorder: trace.NewFlightRecorder(),
}
dbg.cond = sync.NewCond(&dbg.mu)
return dbg
}

func (r *flightRecorder) StartTrace(w http.ResponseWriter, req *http.Request) {
r.mu.Lock()
defer r.mu.Unlock()
if r.recorder.Enabled() {
http.Error(w, "flight recorder is already running", http.StatusConflict)
return
}
if err := r.recorder.Start(); err != nil {
http.Error(w, fmt.Sprintf("could not start flight recorder: %s", err), http.StatusInternalServerError)
return
}
}

func (r *flightRecorder) StopTrace(w http.ResponseWriter, req *http.Request) {
r.mu.Lock()
defer r.mu.Unlock()
if !r.recorder.Enabled() {
http.Error(w, "flight recorder is not running", http.StatusConflict)
return
}
if err := r.recorder.Stop(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

func (r *flightRecorder) SetTracePeriod(w http.ResponseWriter, req *http.Request) {
r.mu.Lock()
defer r.mu.Unlock()
if r.recorder.Enabled() {
http.Error(w, "flight recorder is running, stop it to change its period", http.StatusPreconditionFailed)
return
}
periodValue := req.FormValue("period")
period, err := time.ParseDuration(periodValue)
if err != nil {
http.Error(w, fmt.Sprintf("invalid flight recorder period: %s", err), http.StatusBadRequest)
}
r.recorder.SetPeriod(period)
}

func (r *flightRecorder) Trace(w http.ResponseWriter, req *http.Request) {
r.mu.Lock()
defer r.mu.Unlock()
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", `attachment; filename="trace"`)
if _, err := r.recorder.WriteTo(w); err != nil {
http.Error(w, fmt.Sprintf("could not write in-flight trace: %s", err), http.StatusInternalServerError)
}
}

func setupDebugFlight(m *http.ServeMux) {
r := newFlightRecorder()

const (
flightPattern = "/debug/flight"
flightTracePattern = flightPattern + "/trace"
)

m.HandleFunc("POST "+flightTracePattern+"/start", r.StartTrace)
m.HandleFunc("POST "+flightTracePattern+"/stop", r.StopTrace)
m.HandleFunc("POST "+flightTracePattern+"/set_period", r.SetTracePeriod)
m.HandleFunc("GET "+flightTracePattern, r.Trace)
}
3 changes: 3 additions & 0 deletions control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package control
import (
"context"
"fmt"
"runtime/trace"
"strconv"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -343,6 +344,8 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) {
}

func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) {
defer trace.StartRegion(ctx, "Solve").End()
trace.Logf(ctx, "Request", "solve request: %v", req.Ref)
atomic.AddInt64(&c.buildCount, 1)
defer atomic.AddInt64(&c.buildCount, -1)

Expand Down
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/moby/buildkit

go 1.21.0
go 1.22.0

require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0
Expand Down Expand Up @@ -95,9 +95,10 @@ require (
go.opentelemetry.io/otel/trace v1.21.0
go.opentelemetry.io/proto/otlp v1.0.0
golang.org/x/crypto v0.23.0
golang.org/x/mod v0.17.0
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
golang.org/x/mod v0.21.0
golang.org/x/net v0.25.0
golang.org/x/sync v0.7.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.22.0
golang.org/x/time v0.3.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -450,15 +450,15 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand All @@ -482,8 +482,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -515,8 +515,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
2 changes: 1 addition & 1 deletion hack/dockerfiles/docs-dockerfile.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1

ARG GO_VERSION=1.21
ARG GO_VERSION=1.22
ARG ALPINE_VERSION=3.20

FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest
Expand Down
27 changes: 27 additions & 0 deletions vendor/golang.org/x/exp/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions vendor/golang.org/x/exp/PATENTS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 902ba23

Please sign in to comment.