-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
164 lines (146 loc) · 3.46 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package main
import (
"flag"
"fmt"
"log"
"net"
"sort"
"sync"
"time"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/rpccommon"
)
//DebuggedPID PID of process currently attached tot he debugger
var DebuggedPID = 0
//PidChan Used to PID the PID to whcih we need to attach the debugger
var PidChan = make(chan int)
var port int
var delaySeconds int
var magicKey string
func main() {
flag.IntVar(&port, "port", 2345, "Port used by the Delve server")
flag.IntVar(&delaySeconds, "delay", 3, "Time delay in seconds between each appengine process scan")
flag.StringVar(&magicKey, "key", "", "Magic key to identify a specific module bianry (default is empty string)")
flag.Parse()
// Monitor the appengine modules processes
go func() {
checkAppengineModuleProcess()
for range time.Tick(time.Duration(delaySeconds) * time.Second) {
checkAppengineModuleProcess()
}
}()
// Wait for a PID and attach a new debugger to it
var stopChan chan bool
for pid := range PidChan {
if pid != DebuggedPID && pid != 0 {
if stopChan != nil {
stopChan <- true
waitForFreePort()
}
DebuggedPID = pid
stopChan = attachDelveServer(DebuggedPID)
}
}
}
//checkAppengineModuleProcess llok after the Appengine module process and push the latest new PID into channel
func checkAppengineModuleProcess() {
processes, err := processes()
if err != nil {
log.Fatalln(err.Error())
}
// check each process
pchan := make(chan int)
go func() {
var wg sync.WaitGroup
defer close(pchan)
for _, p := range processes {
if p.Executable() == "_go_app" {
wg.Add(1)
go func(pid int) {
defer wg.Done()
if len(magicKey) == 0 || binaryContainsMagicKey(pid, magicKey) {
pchan <- pid
}
}(p.Pid())
}
}
wg.Wait()
}()
//build the slice of PIDs
pids := sort.IntSlice{}
for pid := range pchan {
pids = append(pids, pid)
}
// keep the youngest one
if len(pids) > 0 {
if len(pids) == 1 {
if pids[0] == DebuggedPID { // already attached to that one
return
}
}
PidChan <- getRecentProcess(pids)
}
}
func waitForFreePort() {
var errCon error
var conn net.Conn
for errCon == nil {
conn, errCon = net.Dial("tcp", fmt.Sprintf(":%d", port))
if errCon == nil {
log.Println("Old server still listening.")
conn.Close()
time.Sleep(1 * time.Second)
}
}
}
func attachDelveServer(attachPid int) chan bool {
stopChan := make(chan bool)
var wgServerRunning sync.WaitGroup
wgServerRunning.Add(1)
go func() {
defer close(stopChan)
// Make a TCP listener
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
for err != nil {
log.Printf("Couldn't start listener: %s\n", err)
time.Sleep(1 * time.Second)
}
defer listener.Close()
// Create and start a debugger server
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{},
AttachPid: attachPid,
AcceptMulti: true,
}, true)
if err := server.Run(); err != nil {
log.Println(err.Error())
} else {
defer server.Stop(false)
}
wgServerRunning.Done()
<-stopChan
}()
//wait for the server to be running
wgServerRunning.Wait()
return stopChan
}
//getRecentProcess within these PIDs which one is the latest one ?
func getRecentProcess(pids sort.IntSlice) int {
if len(pids) == 0 {
return 0
}
tmax := uint64(0)
pid := 0
for _, p := range pids {
t, zombie := getProcessStartTime(p)
if zombie {
continue
}
if t > tmax {
pid = p
tmax = t
}
}
return pid
}