Skip to content

Commit cf7a9aa

Browse files
committed
pprof: take cpu and memory profiles by setting environment variables
When run in standalone mode, the environment variables `DOCKER_BUILDX_CPU_PROFILE` and `DOCKER_BUILDX_MEM_PROFILE` will cause profiles to be written by the CLI. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent 9f0ebd2 commit cf7a9aa

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

cmd/buildx/debug.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"os"
6+
"runtime"
7+
"runtime/pprof"
8+
9+
"github.com/moby/buildkit/util/bklog"
10+
"github.com/sirupsen/logrus"
11+
)
12+
13+
func setupDebugProfiles(ctx context.Context) (stop func()) {
14+
var stopFuncs []func()
15+
if fn := setupCPUProfile(ctx); fn != nil {
16+
stopFuncs = append(stopFuncs, fn)
17+
}
18+
if fn := setupHeapProfile(ctx); fn != nil {
19+
stopFuncs = append(stopFuncs, fn)
20+
}
21+
return func() {
22+
for _, fn := range stopFuncs {
23+
fn()
24+
}
25+
}
26+
}
27+
28+
func setupCPUProfile(ctx context.Context) (stop func()) {
29+
if cpuProfile := os.Getenv("BUILDX_CPU_PROFILE"); cpuProfile != "" {
30+
f, err := os.Create(cpuProfile)
31+
if err != nil {
32+
bklog.G(ctx).Warn("could not create cpu profile", logrus.WithError(err))
33+
return nil
34+
}
35+
36+
if err := pprof.StartCPUProfile(f); err != nil {
37+
bklog.G(ctx).Warn("could not start cpu profile", logrus.WithError(err))
38+
_ = f.Close()
39+
return nil
40+
}
41+
42+
return func() {
43+
pprof.StopCPUProfile()
44+
if err := f.Close(); err != nil {
45+
bklog.G(ctx).Warn("could not close file for cpu profile", logrus.WithError(err))
46+
}
47+
}
48+
}
49+
return nil
50+
}
51+
52+
func setupHeapProfile(ctx context.Context) (stop func()) {
53+
if heapProfile := os.Getenv("BUILDX_MEM_PROFILE"); heapProfile != "" {
54+
// Memory profile is only created on stop.
55+
return func() {
56+
f, err := os.Create(heapProfile)
57+
if err != nil {
58+
bklog.G(ctx).Warn("could not create memory profile", logrus.WithError(err))
59+
return
60+
}
61+
62+
// get up-to-date statistics
63+
runtime.GC()
64+
65+
if err := pprof.WriteHeapProfile(f); err != nil {
66+
bklog.G(ctx).Warn("could not write memory profile", logrus.WithError(err))
67+
}
68+
69+
if err := f.Close(); err != nil {
70+
bklog.G(ctx).Warn("could not close file for memory profile", logrus.WithError(err))
71+
}
72+
}
73+
}
74+
return nil
75+
}

cmd/buildx/main.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,28 @@ func runPlugin(cmd *command.DockerCli) error {
7373
})
7474
}
7575

76+
func run(cmd *command.DockerCli) error {
77+
stopProfiles := setupDebugProfiles(context.TODO())
78+
defer stopProfiles()
79+
80+
if plugin.RunningStandalone() {
81+
return runStandalone(cmd)
82+
}
83+
return runPlugin(cmd)
84+
}
85+
7686
func main() {
7787
cmd, err := command.NewDockerCli()
7888
if err != nil {
7989
fmt.Fprintln(os.Stderr, err)
8090
os.Exit(1)
8191
}
8292

83-
if plugin.RunningStandalone() {
84-
err = runStandalone(cmd)
85-
} else {
86-
err = runPlugin(cmd)
87-
}
88-
if err == nil {
93+
if err = run(cmd); err == nil {
8994
return
9095
}
9196

97+
// Check the error from the run function above.
9298
if sterr, ok := err.(cli.StatusError); ok {
9399
if sterr.Status != "" {
94100
fmt.Fprintln(cmd.Err(), sterr.Status)

0 commit comments

Comments
 (0)