Used new xds agent v1.0.0-rc1 and lib xaapiv1.
[src/xds/xds-gdb.git] / gdb-native.go
1 // +build !windows
2
3 package main
4
5 import (
6         "bufio"
7         "fmt"
8         "os"
9         "os/exec"
10         "syscall"
11         "time"
12
13         "github.com/Sirupsen/logrus"
14         "github.com/kr/pty"
15 )
16
17 type GdbNative struct {
18         log   *logrus.Logger
19         ccmd  string
20         aargs []string
21         eenv  []string
22
23         exeCmd *exec.Cmd
24         fdPty  *os.File
25
26         // callbacks
27         cbOnDisconnect func(error)
28         cbRead         func(timestamp, stdout, stderr string)
29         cbInferiorRead func(timestamp, stdout, stderr string)
30         cbOnExit       func(code int, err error)
31
32         running bool
33 }
34
35 // NewGdbNative creates a new instance of GdbNative
36 func NewGdbNative(log *logrus.Logger, args []string, env []string) *GdbNative {
37         return &GdbNative{
38                 log:   log,
39                 ccmd:  "/usr/bin/gdb",
40                 aargs: args,
41                 eenv:  env,
42         }
43 }
44
45 // SetConfig set additional config fields
46 func (g *GdbNative) SetConfig(name string, value interface{}) error {
47         return fmt.Errorf("Unknown %s field", name)
48 }
49
50 // Init initializes gdb XDS
51 func (g *GdbNative) Init() (int, error) {
52
53         // Create the exec command
54         g.exeCmd = exec.Command(g.ccmd, g.aargs...)
55
56         return 0, nil
57 }
58
59 // Close
60 func (g *GdbNative) Close() error {
61         g.cbOnDisconnect = nil
62         g.cbOnExit = nil
63         g.cbRead = nil
64         g.cbInferiorRead = nil
65
66         g.running = false
67
68         return nil
69 }
70
71 // Start sends a request to start remotely gdb within xds-server
72 func (g *GdbNative) Start(inferiorTTY bool) (int, error) {
73         var err error
74
75         // Start pty and consequently gdb process
76         if g.fdPty, err = pty.Start(g.exeCmd); err != nil {
77                 return int(syscall.ESPIPE), err
78         }
79
80         g.running = true
81
82         // Monitor gdb process EOF
83         go func() {
84                 // Execute command and wait EOF
85                 err := g.exeCmd.Wait()
86                 g.cbOnDisconnect(err)
87                 g.running = false
88         }()
89
90         // Handle STDOUT
91         go func() {
92                 sc := bufio.NewScanner(g.fdPty)
93                 sc.Split(split)
94                 for sc.Scan() {
95                         if g.cbRead != nil {
96                                 g.cbRead(time.Now().String(), sc.Text(), "")
97                         }
98                         if !g.running {
99                                 return
100                         }
101                 }
102         }()
103
104         return 0, nil
105 }
106
107 // Cmd returns the command name
108 func (g *GdbNative) Cmd() string {
109         return g.ccmd
110 }
111
112 // Args returns the list of arguments
113 func (g *GdbNative) Args() []string {
114         return g.aargs
115 }
116
117 // Env returns the list of environment variables
118 func (g *GdbNative) Env() []string {
119         return g.eenv
120 }
121
122 // OnError doesn't make sens
123 func (g *GdbNative) OnError(f func(error)) {
124         // nothing to do
125 }
126
127 // OnDisconnect is called when stdin is disconnected
128 func (g *GdbNative) OnDisconnect(f func(error)) {
129         g.cbOnDisconnect = f
130 }
131
132 // OnExit calls when exit event is received
133 func (g *GdbNative) OnExit(f func(code int, err error)) {
134         g.cbOnExit = f
135 }
136
137 // Read calls when a message/string event is received on stdout or stderr
138 func (g *GdbNative) Read(f func(timestamp, stdout, stderr string)) {
139         g.cbRead = f
140 }
141
142 // InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
143 func (g *GdbNative) InferiorRead(f func(timestamp, stdout, stderr string)) {
144         g.cbInferiorRead = f
145 }
146
147 // Write writes message/string into gdb stdin
148 func (g *GdbNative) Write(args ...interface{}) error {
149         s := fmt.Sprint(args...)
150         _, err := g.fdPty.Write([]byte(s))
151         return err
152 }
153
154 // SendSignal is used to send a signal to remote process/gdb
155 func (g *GdbNative) SendSignal(sig os.Signal) error {
156         if g.exeCmd == nil {
157                 return fmt.Errorf("exeCmd not initialized")
158         }
159         return g.exeCmd.Process.Signal(sig)
160 }
161
162 //***** Private functions *****
163
164 func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
165         if atEOF && len(data) == 0 {
166                 return 0, nil, nil
167         }
168         return len(data), data, nil
169 }