Skip to content

Commit a38c356

Browse files
committed
added a "wait status" debug tool (#68)
This uses a special function name signature pattern to identify points in the code that are waiting for significant events such as reconfigure and initial clones. These points are logged when the program recieves an interrupt signal.
1 parent b95a317 commit a38c356

File tree

3 files changed

+72
-51
lines changed

3 files changed

+72
-51
lines changed

main.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package main
22

33
import (
44
"context"
5-
"log"
5+
"fmt"
66
"os"
77
"os/signal"
8+
"regexp"
89
"runtime"
910
"strings"
1011
"time"
@@ -114,26 +115,32 @@ this repository has new commits, Pico will automatically reconfigure.`,
114115
case err = <-errs:
115116
}
116117

118+
if strings.ToLower(os.Getenv("LOG_LEVEL")) == "debug" {
119+
doTrace()
120+
}
121+
117122
return
118123
},
119124
},
120125
}
121126

122-
if strings.ToLower(os.Getenv("LOG_LEVEL")) == "debug" {
123-
go func() {
124-
sigs := make(chan os.Signal, 1)
125-
signal.Notify(sigs, os.Interrupt)
126-
buf := make([]byte, 1<<20)
127-
for {
128-
<-sigs
129-
stacklen := runtime.Stack(buf, true)
130-
log.Printf("\nPrinting goroutine stack trace because `DEBUG` was set.\n%s\n", buf[:stacklen])
131-
}
132-
}()
133-
}
134-
135127
err := app.Run(os.Args)
136128
if err != nil {
137129
zap.L().Fatal("exit", zap.Error(err))
138130
}
139131
}
132+
133+
var waitpoints = regexp.MustCompile(`__waitpoint__(.+)\(`)
134+
135+
func doTrace() {
136+
buf := make([]byte, 1<<20)
137+
stacklen := runtime.Stack(buf, true)
138+
139+
fmt.Printf("\nPrinting goroutine stack trace because `DEBUG` was set.\n%s\n", buf[:stacklen])
140+
fmt.Println("Code that was waiting:")
141+
142+
for _, s := range waitpoints.FindAllStringSubmatch(string(buf[:stacklen]), 1) {
143+
fmt.Printf(" - %s\n", s[1])
144+
}
145+
fmt.Println("\nSee the docs on https://pico.sh/ for more information.")
146+
}

reconfigurer/git.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,18 @@ func (p *GitProvider) watchConfig() (err error) {
126126
}()
127127
zap.L().Debug("created new config watcher, awaiting setup")
128128

129+
err = p.__waitpoint__watch_config(errs)
130+
131+
zap.L().Debug("config watcher initialised")
132+
133+
return
134+
}
135+
136+
func (p *GitProvider) __waitpoint__watch_config(errs chan error) (err error) {
129137
select {
130138
case <-p.configWatcher.InitialDone:
131139
case err = <-errs:
132140
}
133-
134-
zap.L().Debug("config watcher initialised")
135-
136141
return
137142
}
138143

watcher/git.go

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -60,45 +60,49 @@ func NewGitWatcher(
6060
}
6161
}
6262

63-
// Start runs the watcher loop and blocks until a fatal error occurs
64-
func (w *GitWatcher) Start() error {
65-
zap.L().Debug("git watcher initialising, waiting for first state to be set")
66-
67-
// wait for the first config event to set the initial state
63+
func (w *GitWatcher) __waitpoint__start_wait_init() {
6864
<-w.initialise
65+
}
6966

70-
zap.L().Debug("git watcher initialised", zap.Any("initial_state", w.state))
71-
72-
f := func() (err error) {
73-
select {
74-
case newState := <-w.newState:
75-
zap.L().Debug("git watcher received new state",
76-
zap.Any("new_state", newState))
77-
78-
return w.doReconfigure(newState)
67+
func (w *GitWatcher) __waitpoint__start_select_states() (err error) {
68+
select {
69+
case newState := <-w.newState:
70+
zap.L().Debug("git watcher received new state",
71+
zap.Any("new_state", newState))
7972

80-
case <-w.stateReq:
81-
w.stateRes <- w.state
73+
return w.doReconfigure(newState)
8274

83-
case event := <-w.targetsWatcher.Events:
84-
zap.L().Debug("git watcher received a target event",
85-
zap.Any("new_state", event))
75+
case <-w.stateReq:
76+
w.stateRes <- w.state
8677

87-
if e := w.handle(event); e != nil {
88-
zap.L().Error("failed to handle event",
89-
zap.String("url", event.URL),
90-
zap.Error(e))
91-
}
78+
case event := <-w.targetsWatcher.Events:
79+
zap.L().Debug("git watcher received a target event",
80+
zap.Any("new_state", event))
9281

93-
case e := <-errorMultiplex(w.errors, w.targetsWatcher.Errors):
94-
zap.L().Error("git error",
82+
if e := w.handle(event); e != nil {
83+
zap.L().Error("failed to handle event",
84+
zap.String("url", event.URL),
9585
zap.Error(e))
9686
}
97-
return
87+
88+
case e := <-errorMultiplex(w.errors, w.targetsWatcher.Errors):
89+
zap.L().Error("git error",
90+
zap.Error(e))
9891
}
92+
return
93+
}
94+
95+
// Start runs the watcher loop and blocks until a fatal error occurs
96+
func (w *GitWatcher) Start() error {
97+
zap.L().Debug("git watcher initialising, waiting for first state to be set")
98+
99+
// wait for the first config event to set the initial state
100+
w.__waitpoint__start_wait_init()
101+
102+
zap.L().Debug("git watcher initialised", zap.Any("initial_state", w.state))
99103

100104
for {
101-
err := f()
105+
err := w.__waitpoint__start_select_states()
102106
if err != nil {
103107
return err
104108
}
@@ -203,13 +207,18 @@ func (w *GitWatcher) watchTargets() (err error) {
203207
}()
204208
zap.L().Debug("created targets watcher, awaiting setup")
205209

210+
err = w.__waitpoint__watch_targets(errs)
211+
212+
zap.L().Debug("targets watcher initialised")
213+
214+
return
215+
}
216+
217+
func (w *GitWatcher) __waitpoint__watch_targets(errs chan error) (err error) {
206218
select {
207219
case <-w.targetsWatcher.InitialDone:
208220
case err = <-errs:
209221
}
210-
211-
zap.L().Debug("targets watcher initialised")
212-
213222
return
214223
}
215224

@@ -222,7 +231,7 @@ func (w *GitWatcher) handle(e gitwatch.Event) (err error) {
222231
zap.String("target", target.Name),
223232
zap.String("url", target.RepoURL),
224233
zap.Time("timestamp", e.Timestamp))
225-
w.send(target, e.Path, false)
234+
w.__waitpoint__send_target_task(target, e.Path, false)
226235
return nil
227236
}
228237

@@ -257,7 +266,7 @@ func (w GitWatcher) executeTargets(targets []task.Target, shutdown bool) {
257266
zap.Int("targets", len(targets)))
258267

259268
for _, t := range targets {
260-
w.send(t, filepath.Join(w.directory, t.Name), shutdown)
269+
w.__waitpoint__send_target_task(t, filepath.Join(w.directory, t.Name), shutdown)
261270
}
262271
}
263272

@@ -270,7 +279,7 @@ func (w GitWatcher) getTarget(url string) (target task.Target, exists bool) {
270279
return
271280
}
272281

273-
func (w GitWatcher) send(target task.Target, path string, shutdown bool) {
282+
func (w GitWatcher) __waitpoint__send_target_task(target task.Target, path string, shutdown bool) {
274283
w.bus <- task.ExecutionTask{
275284
Target: target,
276285
Path: path,

0 commit comments

Comments
 (0)