Skip to content

Commit 15ec603

Browse files
authored
chore: add http/pprof server over unix socket for debug (#295) (#296)
* chore: add http/pprof server over unix socket for debug * remove old pprof file without checking if it exists --------- Signed-off-by: Spike Curtis <[email protected]>
1 parent 9c250f1 commit 15ec603

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
1212

1313
func main() {
14+
servePprof()
1415
plugin.Serve(&plugin.ServeOpts{
1516
ProviderFunc: provider.New,
1617
})

pprof_unix.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//go:build !windows
2+
3+
package main
4+
5+
import (
6+
"net"
7+
"net/http"
8+
"net/http/pprof"
9+
"os"
10+
)
11+
12+
// servePprof starts an HTTP server running the pprof goroutine handler on a local unix domain socket. As described in
13+
// https://github.com/coder/coder/issues/14726 it appears this process is sometimes hanging, unable to exit cleanly,
14+
// and this prevents additional Coder builds that try to reinstall this provider. A goroutine dump should allow us to
15+
// determine what is hanging.
16+
//
17+
// This function is best-effort, and just returns early if we fail to set up the directory/listener. We don't want to
18+
// block the normal functioning of the provider.
19+
func servePprof() {
20+
// Coder runs terraform in a per-build subdirectory of the work directory. The per-build subdirectory uses a
21+
// generated name and is deleted at the end of a build, so we want to place our unix socket up one directory level
22+
// in the provisionerd work directory, so we can connect to it from provisionerd.
23+
err := os.Mkdir("../.coder", 0o700)
24+
if err != nil && !os.IsExist(err) {
25+
return
26+
}
27+
28+
// remove the old file, if it exists. It's probably from the last run of the provider
29+
if err = os.Remove("../.coder/pprof"); err != nil && !os.IsNotExist(err) {
30+
return
31+
}
32+
l, err := net.Listen("unix", "../.coder/pprof")
33+
if err != nil {
34+
return
35+
}
36+
mux := http.NewServeMux()
37+
mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
38+
srv := http.Server{Handler: mux}
39+
go srv.Serve(l)
40+
// We just leave the server and domain socket up forever. Go programs exit when the `main()` function returns, so
41+
// this won't block exiting, and it ensures the pprof server stays up for the entire lifetime of the provider.
42+
}

pprof_windows.go

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//go:build windows
2+
3+
package main
4+
5+
// servePprof is not supported on Windows
6+
func servePprof() {}

0 commit comments

Comments
 (0)