Update GOPATH in VSCode project (now in gerrit)
[src/xds/xds-gdb.git] / gdb-native.go
1 // +build !windows
2
3 /*
4  * Copyright (C) 2017-2018 "IoT.bzh"
5  * Author Sebastien Douheret <sebastien@iot.bzh>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */
20
21 package main
22
23 import (
24         "bufio"
25         "fmt"
26         "os"
27         "os/exec"
28         "syscall"
29         "time"
30
31         "github.com/Sirupsen/logrus"
32         "github.com/kr/pty"
33 )
34
35 // GdbNative - Implementation of IGDB used to interfacing native gdb
36 type GdbNative struct {
37         log   *logrus.Logger
38         ccmd  string
39         aargs []string
40         eenv  []string
41
42         exeCmd *exec.Cmd
43         fdPty  *os.File
44
45         // callbacks
46         cbOnDisconnect func(error)
47         cbRead         func(timestamp, stdout, stderr string)
48         cbInferiorRead func(timestamp, stdout, stderr string)
49         cbOnExit       func(code int, err error)
50
51         running bool
52 }
53
54 // NewGdbNative creates a new instance of GdbNative
55 func NewGdbNative(log *logrus.Logger, args []string, env []string) *GdbNative {
56         return &GdbNative{
57                 log:   log,
58                 ccmd:  "/usr/bin/gdb",
59                 aargs: args,
60                 eenv:  env,
61         }
62 }
63
64 // SetConfig set additional config fields
65 func (g *GdbNative) SetConfig(name string, value interface{}) error {
66         return fmt.Errorf("Unknown %s field", name)
67 }
68
69 // Init initializes gdb XDS
70 func (g *GdbNative) Init() (int, error) {
71
72         // Create the exec command
73         g.exeCmd = exec.Command(g.ccmd, g.aargs...)
74
75         return 0, nil
76 }
77
78 // Close frees allocated objects and close opened connections
79 func (g *GdbNative) Close() error {
80         g.cbOnDisconnect = nil
81         g.cbOnExit = nil
82         g.cbRead = nil
83         g.cbInferiorRead = nil
84
85         g.running = false
86
87         return nil
88 }
89
90 // Start sends a request to start remotely gdb within xds-server
91 func (g *GdbNative) Start(inferiorTTY bool) (int, error) {
92         var err error
93
94         // Start pty and consequently gdb process
95         if g.fdPty, err = pty.Start(g.exeCmd); err != nil {
96                 return int(syscall.ESPIPE), err
97         }
98
99         g.running = true
100
101         // Monitor gdb process EOF
102         go func() {
103                 // Execute command and wait EOF
104                 err := g.exeCmd.Wait()
105                 g.cbOnDisconnect(err)
106                 g.running = false
107         }()
108
109         // Handle STDOUT
110         go func() {
111                 sc := bufio.NewScanner(g.fdPty)
112                 sc.Split(split)
113                 for sc.Scan() {
114                         if g.cbRead != nil {
115                                 g.cbRead(time.Now().String(), sc.Text(), "")
116                         }
117                         if !g.running {
118                                 return
119                         }
120                 }
121         }()
122
123         return 0, nil
124 }
125
126 // Cmd returns the command name
127 func (g *GdbNative) Cmd() string {
128         return g.ccmd
129 }
130
131 // Args returns the list of arguments
132 func (g *GdbNative) Args() []string {
133         return g.aargs
134 }
135
136 // Env returns the list of environment variables
137 func (g *GdbNative) Env() []string {
138         return g.eenv
139 }
140
141 // OnError doesn't make sens
142 func (g *GdbNative) OnError(f func(error)) {
143         // nothing to do
144 }
145
146 // OnDisconnect is called when stdin is disconnected
147 func (g *GdbNative) OnDisconnect(f func(error)) {
148         g.cbOnDisconnect = f
149 }
150
151 // OnExit calls when exit event is received
152 func (g *GdbNative) OnExit(f func(code int, err error)) {
153         g.cbOnExit = f
154 }
155
156 // Read calls when a message/string event is received on stdout or stderr
157 func (g *GdbNative) Read(f func(timestamp, stdout, stderr string)) {
158         g.cbRead = f
159 }
160
161 // InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
162 func (g *GdbNative) InferiorRead(f func(timestamp, stdout, stderr string)) {
163         g.cbInferiorRead = f
164 }
165
166 // Write writes message/string into gdb stdin
167 func (g *GdbNative) Write(args ...interface{}) error {
168         s := fmt.Sprint(args...)
169         _, err := g.fdPty.Write([]byte(s))
170         return err
171 }
172
173 // SendSignal is used to send a signal to remote process/gdb
174 func (g *GdbNative) SendSignal(sig os.Signal) error {
175         if g.exeCmd == nil {
176                 return fmt.Errorf("exeCmd not initialized")
177         }
178         return g.exeCmd.Process.Signal(sig)
179 }
180
181 //***** Private functions *****
182
183 func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
184         if atEOF && len(data) == 0 {
185                 return 0, nil, nil
186         }
187         return len(data), data, nil
188 }