aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--.vscode/launch.json57
-rw-r--r--.vscode/settings.json22
-rw-r--r--LICENSE201
-rw-r--r--Makefile121
-rw-r--r--README.md4
-rw-r--r--gdb-common.go21
-rw-r--r--gdb-common_darwin.go42
-rw-r--r--gdb-common_linux.go37
-rw-r--r--gdb-common_windows.go7
-rw-r--r--gdb-native.go164
-rw-r--r--gdb-xds.go311
-rw-r--r--glide.yaml23
-rw-r--r--main.go578
14 files changed, 1597 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..07d6bd4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+bin
+tools
+glide.lock
+vendor
+package
+*.zip
+
+debug
+__*
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..f047dc0
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,57 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+ "name": "xds-gdb help",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["-tt", "--help", "--version"],
+ "showLog": false
+ },
+ {
+ "name": "xds-gdb",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}"
+ },
+ "args": ["-x", "/tmp/gdbconf.ini", "-nx"],
+ "showLog": false
+ },
+ {
+ "name": "xds-gdb TTY",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}"
+ },
+ "args": ["-tty", "/dev/pts27", "-nx", "-x", "${workspaceRoot}/__config/gdb-on-m3ulcb_debug_pi.ini"],
+ "showLog": false
+ },
+ {
+ "name": "xds-gdb native",
+ "type": "go",
+ "request": "launch",
+ "mode": "debug",
+ "program": "${workspaceRoot}",
+ "env": {
+ "GOPATH": "${workspaceRoot}/../../../..:${env:GOPATH}",
+ "XDS_LOGLEVEL": "debug"
+ },
+ "args": ["-x", "${workspaceRoot}/__config/gdb-on-localhost_debug_pi.ini"],
+ "showLog": false
+ }
+
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..c9bea18
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,22 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ // Configure glob patterns for excluding files and folders.
+ "files.exclude": {
+ "**/.tmp": true,
+ ".git": true,
+ "glide.lock": true,
+ "vendor/": true,
+ "debug": true,
+ "bin": true,
+ "tools": true
+ },
+ // Words to add to dictionary for a workspace.
+ "cSpell.words": [
+ "apiv",
+ "iosk",
+ "zhouhui",
+ "ldflags",
+ "socketio",
+ "xdsconfig"
+ ]
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8c58555
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,121 @@
+# Makefile used to build xds-gdb commands
+
+# Application Version
+VERSION := 0.0.1
+
+
+# Retrieve git tag/commit to set sub-version string
+ifeq ($(origin SUB_VERSION), undefined)
+ SUB_VERSION := $(shell git describe --exact-match --tags 2>/dev/null | sed 's/^v//')
+ ifneq ($(SUB_VERSION), )
+ VERSION := $(firstword $(subst -, ,$(SUB_VERSION)))
+ SUB_VERSION := $(word 2,$(subst -, ,$(SUB_VERSION)))
+ else
+ SUB_VERSION := $(shell git rev-parse --short HEAD)
+ ifeq ($(SUB_VERSION), )
+ SUB_VERSION := unknown-dev
+ endif
+ endif
+endif
+
+HOST_GOOS=$(shell go env GOOS)
+HOST_GOARCH=$(shell go env GOARCH)
+ARCH=$(HOST_GOOS)-$(HOST_GOARCH)
+
+EXT=
+ifeq ($(HOST_GOOS), windows)
+ EXT=.exe
+endif
+
+
+mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
+ROOT_SRCDIR := $(patsubst %/,%,$(dir $(mkfile_path)))
+BINDIR := $(ROOT_SRCDIR)/bin
+ROOT_GOPRJ := $(abspath $(ROOT_SRCDIR)/../../../..)
+PACKAGE_DIR := $(ROOT_SRCDIR)/package
+
+export GOPATH := $(shell go env GOPATH):$(ROOT_GOPRJ)
+export PATH := $(PATH):$(ROOT_SRCDIR)/tools
+
+VERBOSE_1 := -v
+VERBOSE_2 := -v -x
+
+# Release or Debug mode
+ifeq ($(filter 1,$(RELEASE) $(REL)),)
+ GORELEASE=
+ BUILD_MODE="Debug mode"
+else
+ # optimized code without debug info
+ GORELEASE= -s -w
+ BUILD_MODE="Release mode"
+endif
+
+REPOPATH=github.com/iotbzh/xds-gdb
+TARGET := xds-gdb
+
+build: $(TARGET)
+
+xds-gdb: vendor
+ @echo "### Build $@ (version $(VERSION), subversion $(SUB_VERSION)) - $(BUILD_MODE)";
+ @cd $(ROOT_SRCDIR); $(BUILD_ENV_FLAGS) go build $(VERBOSE_$(V)) -i -o $(BINDIR)/$@$(EXT) -ldflags "$(GORELEASE) -X main.AppVersion=$(VERSION) -X main.AppSubVersion=$(SUB_VERSION)" .
+ @([ "$(HOST_GOOS)" = "linux" ] && { cd $(BINDIR) && ln -sf $@ $(subst xds-,,$@); } || { true; } )
+
+test: tools/glide
+ go test --race $(shell ./tools/glide novendor)
+
+vet: tools/glide
+ go vet $(shell ./tools/glide novendor)
+
+fmt: tools/glide
+ go fmt $(shell ./tools/glide novendor)
+
+.PHONY: clean
+clean:
+ rm -rf $(BINDIR)/* debug $(ROOT_GOPRJ)/pkg/*/$(REPOPATH) $(PACKAGE_DIR)
+
+distclean: clean
+ rm -rf $(BINDIR) tools glide.lock vendor $(ROOT_SRCDIR)/*.zip
+
+.PHONY: release
+release:
+ RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile clean build
+
+package: clean build
+ @mkdir -p $(PACKAGE_DIR)/xds-gdb
+ @cp -a $(BINDIR)/*gdb$(EXT) $(PACKAGE_DIR)/xds-gdb
+ @cd $(PACKAGE_DIR) && zip --symlinks -r $(ROOT_SRCDIR)/xds-gdb_$(ARCH)-v$(VERSION)_$(SUB_VERSION).zip ./xds-gdb
+
+.PHONY: package-all
+package-all:
+ @echo "# Build linux amd64..."
+ GOOS=linux GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+ @echo "# Build windows amd64..."
+# GOOS=windows GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+ @echo " WARNING: build on Windows not supported for now."
+ @echo "# Build darwin amd64..."
+ GOOS=darwin GOARCH=amd64 RELEASE=1 make -f $(ROOT_SRCDIR)/Makefile package
+
+vendor: tools/glide glide.yaml
+ ./tools/glide install --strip-vendor
+
+vendor/debug: vendor
+ (cd vendor/github.com/iotbzh && \
+ rm -rf xds-common && ln -s ../../../../xds-common && \
+ rm -rf xds-server && ln -s ../../../../xds-server )
+
+tools/glide:
+ @echo "Downloading glide"
+ mkdir -p tools
+ curl --silent -L https://glide.sh/get | GOBIN=./tools sh
+
+help:
+ @echo "Main supported rules:"
+ @echo " build (default)"
+ @echo " release"
+ @echo " clean"
+ @echo " package"
+ @echo " distclean"
+ @echo ""
+ @echo "Influential make variables:"
+ @echo " V - Build verbosity {0,1,2}."
+ @echo " BUILD_ENV_FLAGS - Environment added to 'go build'."
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..389c03d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+xds-gdb: wrapper on gdb for XDS
+=================================
+
+`xds-gdb` is a wrapper on gdb debugger for X(cross) Development System.
diff --git a/gdb-common.go b/gdb-common.go
new file mode 100644
index 0000000..a6984cc
--- /dev/null
+++ b/gdb-common.go
@@ -0,0 +1,21 @@
+package main
+
+import "os"
+
+// IGDB is an interface for GDB
+type IGDB interface {
+ Init() (int, error)
+ Close() error
+ SetConfig(name string, value interface{}) error
+ Start(bool) (int, error)
+ Cmd() string
+ Args() []string
+ Env() []string
+ OnError(f func(error))
+ OnDisconnect(f func(error))
+ OnExit(f func(int, error))
+ Read(f func(timestamp, stdout, stderr string))
+ InferiorRead(f func(timestamp, stdout, stderr string))
+ Write(args ...interface{}) error
+ SendSignal(sig os.Signal) error
+}
diff --git a/gdb-common_darwin.go b/gdb-common_darwin.go
new file mode 100644
index 0000000..d167c5d
--- /dev/null
+++ b/gdb-common_darwin.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+const (
+ syscallEBADE = syscall.EBADEXEC
+
+ syscall_TCGETS = 0x402c7413
+ syscall_TCSETS = 0x802c7414
+)
+
+func fcntl(fd uintptr, cmd int, arg int) (val int, err error) {
+ r, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
+ uintptr(arg))
+ val = int(r)
+ if e != 0 {
+ err = e
+ }
+ return
+}
+
+func tcsetattr(fd uintptr, termios *syscall.Termios) error {
+ r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+ fd, uintptr(syscall_TCSETS), uintptr(unsafe.Pointer(termios)))
+ if r != 0 {
+ return os.NewSyscallError("SYS_IOCTL", e)
+ }
+ return nil
+}
+
+func tcgetattr(fd uintptr, termios *syscall.Termios) error {
+ r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+ fd, uintptr(syscall_TCGETS), uintptr(unsafe.Pointer(termios)))
+ if r != 0 {
+ return os.NewSyscallError("SYS_IOCTL", e)
+ }
+ return nil
+}
diff --git a/gdb-common_linux.go b/gdb-common_linux.go
new file mode 100644
index 0000000..a2f4bf6
--- /dev/null
+++ b/gdb-common_linux.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "os"
+ "syscall"
+ "unsafe"
+)
+
+const syscallEBADE = syscall.EBADE
+
+func fcntl(fd uintptr, cmd int, arg int) (val int, err error) {
+ r, _, e := syscall.Syscall(syscall.SYS_FCNTL, fd, uintptr(cmd),
+ uintptr(arg))
+ val = int(r)
+ if e != 0 {
+ err = e
+ }
+ return
+}
+
+func tcsetattr(fd uintptr, termios *syscall.Termios) error {
+ r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+ fd, uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(termios)))
+ if r != 0 {
+ return os.NewSyscallError("SYS_IOCTL", e)
+ }
+ return nil
+}
+
+func tcgetattr(fd uintptr, termios *syscall.Termios) error {
+ r, _, e := syscall.Syscall(syscall.SYS_IOCTL,
+ fd, uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(termios)))
+ if r != 0 {
+ return os.NewSyscallError("SYS_IOCTL", e)
+ }
+ return nil
+}
diff --git a/gdb-common_windows.go b/gdb-common_windows.go
new file mode 100644
index 0000000..b233943
--- /dev/null
+++ b/gdb-common_windows.go
@@ -0,0 +1,7 @@
+package main
+
+import "syscall"
+
+const (
+ syscallEBADE = syscall.EBADE
+)
diff --git a/gdb-native.go b/gdb-native.go
new file mode 100644
index 0000000..a4e6189
--- /dev/null
+++ b/gdb-native.go
@@ -0,0 +1,164 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/kr/pty"
+)
+
+type GdbNative struct {
+ log *logrus.Logger
+ ccmd string
+ aargs []string
+ eenv []string
+
+ exeCmd *exec.Cmd
+ fdPty *os.File
+
+ // callbacks
+ cbOnDisconnect func(error)
+ cbRead func(timestamp, stdout, stderr string)
+ cbInferiorRead func(timestamp, stdout, stderr string)
+ cbOnExit func(code int, err error)
+
+ running bool
+}
+
+// NewGdbNative creates a new instance of GdbNative
+func NewGdbNative(log *logrus.Logger, args []string, env []string) *GdbNative {
+ return &GdbNative{
+ log: log,
+ ccmd: "/usr/bin/gdb",
+ aargs: args,
+ eenv: env,
+ }
+}
+
+// SetConfig set additional config fields
+func (g *GdbNative) SetConfig(name string, value interface{}) error {
+ return fmt.Errorf("Unknown %s field", name)
+}
+
+// Init initializes gdb XDS
+func (g *GdbNative) Init() (int, error) {
+
+ // Create the exec command
+ g.exeCmd = exec.Command(g.ccmd, g.aargs...)
+
+ return 0, nil
+}
+
+// Close
+func (g *GdbNative) Close() error {
+ g.cbOnDisconnect = nil
+ g.cbOnExit = nil
+ g.cbRead = nil
+ g.cbInferiorRead = nil
+
+ g.running = false
+
+ return nil
+}
+
+// Start sends a request to start remotely gdb within xds-server
+func (g *GdbNative) Start(inferiorTTY bool) (int, error) {
+ var err error
+
+ // Start pty and consequently gdb process
+ if g.fdPty, err = pty.Start(g.exeCmd); err != nil {
+ return int(syscall.ESPIPE), err
+ }
+
+ g.running = true
+
+ // Monitor gdb process EOF
+ go func() {
+ // Execute command and wait EOF
+ err := g.exeCmd.Wait()
+ g.cbOnDisconnect(err)
+ g.running = false
+ }()
+
+ // Handle STDOUT
+ go func() {
+ sc := bufio.NewScanner(g.fdPty)
+ sc.Split(split)
+ for sc.Scan() {
+ if g.cbRead != nil {
+ g.cbRead(time.Now().String(), sc.Text(), "")
+ }
+ if !g.running {
+ return
+ }
+ }
+ }()
+
+ return 0, nil
+}
+
+// Cmd returns the command name
+func (g *GdbNative) Cmd() string {
+ return g.ccmd
+}
+
+// Args returns the list of arguments
+func (g *GdbNative) Args() []string {
+ return g.aargs
+}
+
+// Env returns the list of environment variables
+func (g *GdbNative) Env() []string {
+ return g.eenv
+}
+
+// OnError doesn't make sens
+func (g *GdbNative) OnError(f func(error)) {
+ // nothing to do
+}
+
+// OnDisconnect is called when stdin is disconnected
+func (g *GdbNative) OnDisconnect(f func(error)) {
+ g.cbOnDisconnect = f
+}
+
+// OnExit calls when exit event is received
+func (g *GdbNative) OnExit(f func(code int, err error)) {
+ g.cbOnExit = f
+}
+
+// Read calls when a message/string event is received on stdout or stderr
+func (g *GdbNative) Read(f func(timestamp, stdout, stderr string)) {
+ g.cbRead = f
+}
+
+// InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
+func (g *GdbNative) InferiorRead(f func(timestamp, stdout, stderr string)) {
+ g.cbInferiorRead = f
+}
+
+// Write writes message/string into gdb stdin
+func (g *GdbNative) Write(args ...interface{}) error {
+ s := fmt.Sprint(args...)
+ _, err := g.fdPty.Write([]byte(s))
+ return err
+}
+
+// SendSignal is used to send a signal to remote process/gdb
+func (g *GdbNative) SendSignal(sig os.Signal) error {
+ return g.exeCmd.Process.Signal(sig)
+}
+
+//***** Private functions *****
+
+func split(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ return len(data), data, nil
+}
diff --git a/gdb-xds.go b/gdb-xds.go
new file mode 100644
index 0000000..f174017
--- /dev/null
+++ b/gdb-xds.go
@@ -0,0 +1,311 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "syscall"
+
+ "github.com/Sirupsen/logrus"
+ common "github.com/iotbzh/xds-common/golib"
+ "github.com/iotbzh/xds-server/lib/apiv1"
+ "github.com/iotbzh/xds-server/lib/crosssdk"
+ "github.com/iotbzh/xds-server/lib/xdsconfig"
+ sio_client "github.com/zhouhui8915/go-socket.io-client"
+)
+
+// GdbXds -
+type GdbXds struct {
+ log *logrus.Logger
+ ccmd string
+ aargs []string
+ eenv []string
+ uri string
+ prjID string
+ sdkID string
+ rPath string
+ listPrj bool
+ cmdID string
+
+ httpCli *common.HTTPClient
+ ioSock *sio_client.Client
+
+ // callbacks
+ cbOnError func(error)
+ cbOnDisconnect func(error)
+ cbRead func(timestamp, stdout, stderr string)
+ cbInferiorRead func(timestamp, stdout, stderr string)
+ cbOnExit func(code int, err error)
+}
+
+// NewGdbXds creates a new instance of GdbXds
+func NewGdbXds(log *logrus.Logger, args []string, env []string) *GdbXds {
+ return &GdbXds{
+ log: log,
+ ccmd: "$GDB", // var set by environment-setup-xxx script
+ aargs: args,
+ eenv: env,
+ httpCli: nil,
+ ioSock: nil,
+ }
+}
+
+// SetConfig set additional config fields
+func (g *GdbXds) SetConfig(name string, value interface{}) error {
+ switch name {
+ case "uri":
+ g.uri = value.(string)
+ case "prjID":
+ g.prjID = value.(string)
+ case "sdkID":
+ g.sdkID = value.(string)
+ case "rPath":
+ g.rPath = value.(string)
+ case "listProject":
+ g.listPrj = value.(bool)
+ default:
+ return fmt.Errorf("Unknown %s field", name)
+ }
+ return nil
+}
+
+// Init initializes gdb XDS
+func (g *GdbXds) Init() (int, error) {
+
+ // Define HTTP and WS url
+ baseURL := g.uri
+ if !strings.HasPrefix(g.uri, "http://") {
+ baseURL = "http://" + g.uri
+ }
+
+ // Create HTTP client
+ g.log.Infoln("Connect HTTP client on ", baseURL)
+ conf := common.HTTPClientConfig{
+ URLPrefix: "/api/v1",
+ HeaderClientKeyName: "XDS-SID",
+ CsrfDisable: true,
+ }
+ c, err := common.HTTPNewClient(baseURL, conf)
+ if err != nil {
+ return int(syscallEBADE), err
+ }
+ g.httpCli = c
+
+ // First call to check that xds-server is alive
+ var data []byte
+ if err := c.HTTPGet("/folders", &data); err != nil {
+ return int(syscallEBADE), err
+ }
+ g.log.Infof("Result of /folders: %v", string(data[:]))
+
+ // Check mandatory args
+ if g.prjID == "" || g.listPrj {
+ return getProjectsList(c, g.log, data)
+ }
+
+ // Create io Websocket client
+ g.log.Infoln("Connecting IO.socket client on ", baseURL)
+
+ opts := &sio_client.Options{
+ Transport: "websocket",
+ Header: make(map[string][]string),
+ }
+ opts.Header["XDS-SID"] = []string{c.GetClientID()}
+
+ iosk, err := sio_client.NewClient(baseURL, opts)
+ if err != nil {
+ e := fmt.Sprintf("IO.socket connection error: " + err.Error())
+ return int(syscall.ECONNABORTED), fmt.Errorf(e)
+ }
+ g.ioSock = iosk
+
+ iosk.On("error", func(err error) {
+ if g.cbOnError != nil {
+ g.cbOnError(err)
+ }
+ })
+
+ iosk.On("disconnection", func(err error) {
+ if g.cbOnDisconnect != nil {
+ g.cbOnDisconnect(err)
+ }
+ })
+
+ iosk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) {
+ if g.cbRead != nil {
+ g.cbRead(ev.Timestamp, ev.Stdout, ev.Stderr)
+ }
+ })
+
+ iosk.On(apiv1.ExecInferiorOutEvent, func(ev apiv1.ExecOutMsg) {
+ if g.cbInferiorRead != nil {
+ g.cbInferiorRead(ev.Timestamp, ev.Stdout, ev.Stderr)
+ }
+ })
+
+ iosk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) {
+ if g.cbOnExit != nil {
+ g.cbOnExit(ev.Code, ev.Error)
+ }
+ })
+
+ return 0, nil
+}
+
+func (g *GdbXds) Close() error {
+ g.cbOnDisconnect = nil
+ g.cbOnError = nil
+ g.cbOnExit = nil
+ g.cbRead = nil
+ g.cbInferiorRead = nil
+
+ return nil
+}
+
+// Start sends a request to start remotely gdb within xds-server
+func (g *GdbXds) Start(inferiorTTY bool) (int, error) {
+ var body []byte
+ var err error
+
+ // Enable workaround about inferior output with gdbserver connection
+ // except if XDS_GDBSERVER_OUTPUT_NOFIX is defined
+ _, gdbserverNoFix := os.LookupEnv("XDS_GDBSERVER_OUTPUT_NOFIX")
+
+ args := apiv1.ExecArgs{
+ ID: g.prjID,
+ SdkID: g.sdkID,
+ Cmd: g.ccmd,
+ Args: g.aargs,
+ Env: g.eenv,
+ RPath: g.rPath,
+ TTY: inferiorTTY,
+ TTYGdbserverFix: !gdbserverNoFix,
+ CmdTimeout: -1, // no timeout, end when stdin close or command exited normally
+ }
+ body, err = json.Marshal(args)
+ if err != nil {
+ return int(syscallEBADE), err
+ }
+
+ g.log.Infof("POST %s/exec %v", g.uri, string(body))
+ var res *http.Response
+ var found bool
+ res, err = g.httpCli.HTTPPostWithRes("/exec", string(body))
+ if err != nil {
+ return int(syscall.EAGAIN), err
+ }
+ dRes := make(map[string]interface{})
+ json.Unmarshal(g.httpCli.ResponseToBArray(res), &dRes)
+ if _, found = dRes["cmdID"]; !found {
+ return int(syscallEBADE), err
+ }
+ g.cmdID = dRes["cmdID"].(string)
+
+ return 0, nil
+}
+
+// Cmd returns the command name
+func (g *GdbXds) Cmd() string {
+ return g.ccmd
+}
+
+// Args returns the list of arguments
+func (g *GdbXds) Args() []string {
+ return g.aargs
+}
+
+// Env returns the list of environment variables
+func (g *GdbXds) Env() []string {
+ return g.eenv
+}
+
+// OnError is called on a WebSocket error
+func (g *GdbXds) OnError(f func(error)) {
+ g.cbOnError = f
+}
+
+// OnDisconnect is called when WebSocket disconnection
+func (g *GdbXds) OnDisconnect(f func(error)) {
+ g.cbOnDisconnect = f
+}
+
+// OnExit calls when exit event is received
+func (g *GdbXds) OnExit(f func(code int, err error)) {
+ g.cbOnExit = f
+}
+
+// Read calls when a message/string event is received on stdout or stderr
+func (g *GdbXds) Read(f func(timestamp, stdout, stderr string)) {
+ g.cbRead = f
+}
+
+// InferiorRead calls when a message/string event is received on stdout or stderr of the debugged program (IOW inferior)
+func (g *GdbXds) InferiorRead(f func(timestamp, stdout, stderr string)) {
+ g.cbInferiorRead = f
+}
+
+// Write writes message/string into gdb stdin
+func (g *GdbXds) Write(args ...interface{}) error {
+ return g.ioSock.Emit(apiv1.ExecInEvent, args...)
+}
+
+// SendSignal is used to send a signal to remote process/gdb
+func (g *GdbXds) SendSignal(sig os.Signal) error {
+ var body []byte
+ body, err := json.Marshal(apiv1.ExecSignalArgs{
+ CmdID: g.cmdID,
+ Signal: sig.String(),
+ })
+ if err != nil {
+ g.log.Errorf(err.Error())
+ }
+ g.log.Debugf("POST /signal %s", string(body))
+ return g.httpCli.HTTPPost("/signal", string(body))
+}
+
+//***** Private functions *****
+
+func getProjectsList(c *common.HTTPClient, log *logrus.Logger, prjData []byte) (int, error) {
+
+ folders := xdsconfig.FoldersConfig{}
+ errMar := json.Unmarshal(prjData, &folders)
+ msg := ""
+ if errMar == nil {
+ msg += "List of existing projects (use: export XDS_PROJECT_ID=<< ID >>): \n"
+ msg += " ID\t\t\t\t | Label"
+ for _, f := range folders {
+ msg += fmt.Sprintf("\n %s\t | %s", f.ID, f.Label)
+ if f.DefaultSdk != "" {
+ msg += fmt.Sprintf("\t(default SDK: %s)", f.DefaultSdk)
+ }
+ }
+ msg += "\n"
+ }
+
+ var data []byte
+ if err := c.HTTPGet("/sdks", &data); err != nil {
+ return int(syscallEBADE), err
+ }
+ log.Infof("Result of /sdks: %v", string(data[:]))
+
+ sdks := []crosssdk.SDK{}
+ errMar = json.Unmarshal(data, &sdks)
+ if errMar == nil {
+ msg += "\nList of installed cross SDKs (use: export XDS_SDK_ID=<< ID >>): \n"
+ msg += " ID\t\t\t\t\t | NAME\n"
+ for _, s := range sdks {
+ msg += fmt.Sprintf(" %s\t | %s\n", s.ID, s.Name)
+ }
+ }
+
+ if len(folders) > 0 && len(sdks) > 0 {
+ msg += fmt.Sprintf("\n")
+ msg += fmt.Sprintf("For example: \n")
+ msg += fmt.Sprintf(" XDS_PROJECT_ID=%q XDS_SDK_ID=%q %s -x myGdbConf.ini\n",
+ folders[0].ID, sdks[0].ID, AppName)
+ }
+
+ return 0, fmt.Errorf(msg)
+}
diff --git a/glide.yaml b/glide.yaml
new file mode 100644
index 0000000..f368862
--- /dev/null
+++ b/glide.yaml
@@ -0,0 +1,23 @@
+package: github.com/iotbzh/xds-gdb
+license: Apache-2.0
+owners:
+- name: Sebastien Douheret
+ email: sebastien@iot.bzh
+import:
+- package: github.com/codegangsta/cli
+ version: ^1.19.1
+- package: github.com/Sirupsen/logrus
+ version: ^0.11.5
+- package: github.com/zhouhui8915/go-socket.io-client
+- package: github.com/iotbzh/xds-server
+ subpackages:
+ - apiv1
+ - crosssdk
+ - xdsconfig
+- package: github.com/iotbzh/xds-common
+ subpackages:
+ - golib/common
+- package: github.com/joho/godotenv
+ version: ^1.1.0
+ subpackages:
+ - cmd/godotenv
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..01b46c9
--- /dev/null
+++ b/main.go
@@ -0,0 +1,578 @@
+// xds-gdb: a wrapper on gdb tool for X(cross) Development System.
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/signal"
+ "os/user"
+ "syscall"
+ "time"
+
+ "strings"
+
+ "path"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/codegangsta/cli"
+ common "github.com/iotbzh/xds-common/golib"
+ "github.com/joho/godotenv"
+)
+
+var appAuthors = []cli.Author{
+ cli.Author{Name: "Sebastien Douheret", Email: "sebastien@iot.bzh"},
+}
+
+// AppName name of this application
+var AppName = "xds-gdb"
+
+// AppVersion Version of this application
+// (set by Makefile)
+var AppVersion = "?.?.?"
+
+// AppSubVersion is the git tag id added to version string
+// Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
+// (set by Makefile)
+var AppSubVersion = "unknown-dev"
+
+// Create logger
+var log = logrus.New()
+var earlyLog = []string{}
+
+// Application details
+const (
+ appCopyright = "Apache-2.0"
+ defaultLogLevel = "warning"
+)
+
+// Exit events
+type exitResult struct {
+ error error
+ code int
+}
+
+// EnvVar - Environment variables used by application
+type EnvVar struct {
+ Name string
+ Usage string
+ Destination *string
+}
+
+// exitError terminates this program with the specified error
+func exitError(code syscall.Errno, f string, a ...interface{}) {
+ err := fmt.Sprintf(f, a...)
+ fmt.Fprintf(os.Stderr, err+"\n")
+ os.Exit(int(code))
+}
+
+// main
+func main() {
+ var uri, prjID, rPath, logLevel, logFile, sdkid, confFile, gdbNative string
+ var listProject bool
+ var err error
+
+ uri = "localhost:8000"
+ logLevel = defaultLogLevel
+
+ // Create a new App instance
+ app := cli.NewApp()
+ app.Name = AppName
+ app.Usage = "wrapper on gdb for X(cross) Development System."
+ app.Version = AppVersion + " (" + AppSubVersion + ")"
+ app.Authors = appAuthors
+ app.Copyright = appCopyright
+ app.Metadata = make(map[string]interface{})
+ app.Metadata["version"] = AppVersion
+ app.Metadata["git-tag"] = AppSubVersion
+ app.Metadata["logger"] = log
+
+ app.Flags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "list, ls",
+ Usage: "list existing xds projects",
+ Destination: &listProject,
+ },
+ }
+
+ appEnvVars := []EnvVar{
+ EnvVar{
+ Name: "XDS_CONFIG",
+ Usage: "env config file to source on startup",
+ Destination: &confFile,
+ },
+ EnvVar{
+ Name: "XDS_LOGLEVEL",
+ Usage: "logging level (supported levels: panic, fatal, error, warn, info, debug)",
+ Destination: &logLevel,
+ },
+ EnvVar{
+ Name: "XDS_LOGFILE",
+ Usage: "logging file",
+ Destination: &logFile,
+ },
+ EnvVar{
+ Name: "XDS_NATIVE_GDB",
+ Usage: "use native gdb instead of remote XDS server",
+ Destination: &gdbNative,
+ },
+ EnvVar{
+ Name: "XDS_PROJECT_ID",
+ Usage: "project ID you want to build (mandatory variable)",
+ Destination: &prjID,
+ },
+ EnvVar{
+ Name: "XDS_RPATH",
+ Usage: "relative path into project",
+ Destination: &rPath,
+ },
+ EnvVar{
+ Name: "XDS_SDK_ID",
+ Usage: "Cross Sdk ID to use to build project",
+ Destination: &sdkid,
+ },
+ EnvVar{
+ Name: "XDS_SERVER_URL",
+ Usage: "remote XDS server url",
+ Destination: &uri,
+ },
+ }
+
+ // Process gdb arguments
+ args := make([]string, len(os.Args))
+ args[0] = os.Args[0]
+ gdbArgs := make([]string, len(os.Args))
+
+ // Split xds-xxx options from gdb options
+ copy(gdbArgs, os.Args[1:])
+ for idx, a := range os.Args[1:] {
+ // Specific case to print help or version of xds-gdb
+ switch a {
+ case "--help", "-h", "--version", "-v":
+ args[1] = a
+ goto endloop
+ case "--":
+ // Detect skip option (IOW '--') to split arguments
+ copy(args, os.Args[0:idx+1])
+ copy(gdbArgs, os.Args[idx+2:])
+ goto endloop
+ }
+ }
+endloop:
+
+ // Parse gdb arguments to detect:
+ // --tty option: used for inferior/ tty of debugged program
+ // -x/--command option: XDS env vars may be set within gdb command file
+ clientPty := ""
+ gdbCmdFile := ""
+ for idx, a := range gdbArgs {
+ switch {
+ case strings.HasPrefix(a, "--tty="):
+ clientPty = a[len("--tty="):]
+ gdbArgs[idx] = ""
+
+ case a == "--tty":
+ case strings.HasPrefix(a, "-tty"):
+ clientPty = gdbArgs[idx+1]
+ gdbArgs[idx] = ""
+ gdbArgs[idx+1] = ""
+
+ case strings.HasPrefix(a, "--command="):
+ gdbCmdFile = a[len("--command="):]
+
+ case a == "--command":
+ case strings.HasPrefix(a, "-x"):
+ gdbCmdFile = gdbArgs[idx+1]
+ }
+ }
+
+ // Source config env file
+ // (we cannot use confFile var because env variables setting is just after)
+ envMap, confFile, err := loadConfigEnvFile(os.Getenv("XDS_CONFIG"), gdbCmdFile)
+ if err != nil {
+ exitError(syscall.ENOENT, err.Error())
+ }
+
+ // Managed env vars and create help
+ dynDesc := "\nENVIRONMENT VARIABLES:"
+ for _, ev := range appEnvVars {
+ dynDesc += fmt.Sprintf("\n %s \t\t %s", ev.Name, ev.Usage)
+ if evVal, evExist := os.LookupEnv(ev.Name); evExist && ev.Destination != nil {
+ *ev.Destination = evVal
+ }
+ }
+ app.Description = "gdb wrapper for X(cross) Development System\n"
+ app.Description += "\n"
+ app.Description += " Two debugging models are supported:\n"
+ app.Description += " - xds remote debugging requiring an XDS server and allowing cross debug\n"
+ app.Description += " - native debugging\n"
+ app.Description += " By default xds remote debug is used and you need to define XDS_NATIVE_GDB to\n"
+ app.Description += " use native gdb debug mode instead.\n"
+ app.Description += "\n"
+ app.Description += " xds-gdb configuration (see variables list below) can be set using:\n"
+ app.Description += " - a config file (XDS_CONFIG)\n"
+ app.Description += " - or environment variables\n"
+ app.Description += " - or by setting variables within gdb ini file (commented line including :XDS-ENV: tag)\n"
+ app.Description += " Example of gdb ini file where we define project and sdk ID:\n"
+ app.Description += " # :XDS-ENV: XDS_PROJECT_ID=IW7B4EE-DBY4Z74_myProject\n"
+ app.Description += " # :XDS-ENV: XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot\n"
+ app.Description += "\n"
+ app.Description += dynDesc + "\n"
+
+ // only one action
+ app.Action = func(ctx *cli.Context) error {
+ var err error
+ curDir, _ := os.Getwd()
+
+ // Set logger level, formatter and log file
+ if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
+ msg := fmt.Sprintf("Invalid log level : \"%v\"\n", logLevel)
+ return cli.NewExitError(msg, int(syscall.EINVAL))
+ }
+ log.Formatter = &logrus.TextFormatter{}
+
+ // Always log into a file
+ if logFile == "" {
+ logFile = "/tmp/xds-gdb.log"
+ }
+ fdL, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ if err != nil {
+ msgErr := fmt.Sprintf("Cannot create log file %s", logFile)
+ return cli.NewExitError(msgErr, int(syscall.EPERM))
+ }
+ log.Out = fdL
+
+ // Build env variables
+ env := []string{}
+ for k, v := range envMap {
+ env = append(env, k+"="+v)
+ }
+
+ // Create cross or native gdb interface
+ var gdb IGDB
+ if gdbNative != "" {
+ gdb = NewGdbNative(log, gdbArgs, env)
+ } else {
+ gdb = NewGdbXds(log, gdbArgs, env)
+ gdb.SetConfig("uri", uri)
+ gdb.SetConfig("prjID", prjID)
+ gdb.SetConfig("sdkID", sdkid)
+ gdb.SetConfig("rPath", rPath)
+ gdb.SetConfig("listProject", listProject)
+ }
+
+ // Log early print
+ for _, msg := range earlyLog {
+ log.Debugf(msg)
+ }
+
+ // Log useful info
+ log.Infof("Original arguments: %v", os.Args)
+ log.Infof("Current directory : %v", curDir)
+ log.Infof("Use confFile : '%s'", confFile)
+ log.Infof("Execute : /exec %v %v", gdb.Cmd(), gdb.Args())
+
+ // Properly report invalid init file error
+ gdbCommandFileError := ""
+ for i, a := range gdbArgs {
+ if a == "-x" {
+ gdbCommandFileError = gdbArgs[i+1] + ": No such file or directory."
+ break
+ } else if strings.HasPrefix(a, "--command=") {
+ gdbCommandFileError = strings.TrimLeft(a, "--command=") + ": No such file or directory."
+ break
+ }
+ }
+ log.Infof("Add detection of error: <%s>", gdbCommandFileError)
+
+ // Init gdb subprocess management
+ if code, err := gdb.Init(); err != nil {
+ return cli.NewExitError(err.Error(), code)
+ }
+
+ exitChan := make(chan exitResult, 1)
+
+ gdb.OnError(func(err error) {
+ fmt.Println("ERROR: ", err.Error())
+ })
+
+ gdb.OnDisconnect(func(err error) {
+ fmt.Println("Disconnection: ", err.Error())
+ exitChan <- exitResult{err, int(syscall.ESHUTDOWN)}
+ })
+
+ gdb.Read(func(timestamp, stdout, stderr string) {
+ if stdout != "" {
+ fmt.Printf("%s", stdout)
+ log.Debugf("Recv OUT: <%s>", stdout)
+ }
+ if stderr != "" {
+ fmt.Fprintf(os.Stderr, "%s", stderr)
+ log.Debugf("Recv ERR: <%s>", stderr)
+ }
+
+ // Correctly report error about init file
+ if gdbCommandFileError != "" && strings.Contains(stdout, gdbCommandFileError) {
+ fmt.Fprintf(os.Stderr, "ERROR: "+gdbCommandFileError)
+ log.Errorf("ERROR: " + gdbCommandFileError)
+ if err := gdb.SendSignal(syscall.SIGTERM); err != nil {
+ log.Errorf("Error while sending signal: %s", err.Error())
+ }
+ exitChan <- exitResult{err, int(syscall.ENOENT)}
+ }
+ })
+
+ gdb.OnExit(func(code int, err error) {
+ exitChan <- exitResult{err, code}
+ })
+
+ // Handle client tty / pts
+ if clientPty != "" {
+ log.Infoln("Client tty detected: %v\n", clientPty)
+
+ cpFd, err := os.OpenFile(clientPty, os.O_RDWR, 0)
+ if err != nil {
+ return cli.NewExitError(err.Error(), int(syscall.EPERM))
+ }
+ defer cpFd.Close()
+
+ // client tty stdin
+ /* XXX TODO - implement stdin to send data to debugged program
+ go func() {
+ reader := bufio.NewReader(cpFd)
+ sc := bufio.NewScanner(reader)
+ for sc.Scan() {
+ data := sc.Text()
+ iosk.Emit(apiv1.ExecInferiorInEvent, data+"\n")
+ log.Debugf("Inferior IN: <%v>", data)
+ }
+ if sc.Err() != nil {
+ log.Warnf("Inferior Stdin scanner exit, close stdin (err=%v)", sc.Err())
+ }
+ }()
+ */
+
+ // client tty stdout
+ gdb.InferiorRead(func(timestamp, stdout, stderr string) {
+ if stdout != "" {
+ fmt.Fprintf(cpFd, "%s", stdout)
+ log.Debugf("Inferior OUT: <%s>", stdout)
+ }
+ if stderr != "" {
+ fmt.Fprintf(cpFd, "%s", stderr)
+ log.Debugf("Inferior ERR: <%s>", stderr)
+ }
+ })
+ }
+
+ // Allow to overwrite some gdb commands
+ var overwriteMap = make(map[string]string)
+ if overEnv, exist := os.LookupEnv("XDS_OVERWRITE_COMMANDS"); exist {
+ overEnvS := strings.TrimSpace(overEnv)
+ if len(overEnvS) > 0 {
+ // Extract overwrite commands from env variable
+ for _, def := range strings.Split(overEnvS, ",") {
+ if kv := strings.Split(def, ":"); len(kv) == 2 {
+ overwriteMap[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
+ } else {
+ return cli.NewExitError(
+ fmt.Errorf("Invalid definition in XDS_OVERWRITE_COMMANDS (%s)", def),
+ int(syscall.EINVAL))
+ }
+ }
+ }
+ } else {
+ overwriteMap["-exec-run"] = "-exec-continue"
+ overwriteMap["-file-exec-and-symbols"] = "-file-exec-file"
+ }
+ log.Debugf("overwriteMap = %v", overwriteMap)
+
+ // Send stdin though WS
+ go func() {
+ paranoia := 600
+ reader := bufio.NewReader(os.Stdin)
+
+ for {
+ sc := bufio.NewScanner(reader)
+ for sc.Scan() {
+ command := sc.Text()
+
+ // overwrite some commands
+ for key, value := range overwriteMap {
+ if strings.Contains(command, key) {
+ command = strings.Replace(command, key, value, 1)
+ log.Debugf("OVERWRITE %s -> %s", key, value)
+ }
+ }
+ gdb.Write(command + "\n")
+ log.Debugf("Send: <%v>", command)
+ }
+ log.Infof("Stdin scanner exit, close stdin (err=%v)", sc.Err())
+
+ // CTRL-D exited scanner, so send it explicitly
+ gdb.Write("\x04")
+ time.Sleep(time.Millisecond * 100)
+
+ if paranoia--; paranoia <= 0 {
+ msg := "Abnormal loop detected on stdin"
+ log.Errorf("Abnormal loop detected on stdin")
+ gdb.SendSignal(syscall.SIGTERM)
+ exitChan <- exitResult{fmt.Errorf(msg), int(syscall.ELOOP)}
+ }
+ }
+ }()
+
+ // Handling all Signals
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs)
+
+ go func() {
+ for {
+ sig := <-sigs
+ if err := gdb.SendSignal(sig); err != nil {
+ log.Errorf("Error while sending signal: %s", err.Error())
+ }
+ }
+ }()
+
+ // Start gdb
+ if code, err := gdb.Start(clientPty != ""); err != nil {
+ return cli.NewExitError(err.Error(), code)
+ }
+
+ // Wait exit
+ select {
+ case res := <-exitChan:
+ errStr := ""
+ if res.code == 0 {
+ log.Infoln("Exit successfully")
+ }
+ if res.error != nil {
+ log.Infoln("Exit with ERROR: ", res.error.Error())
+ errStr = res.error.Error()
+ }
+ return cli.NewExitError(errStr, res.code)
+ }
+ }
+
+ app.Run(args)
+}
+
+// loadConfigEnvFile
+func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string, error) {
+ var err error
+ envMap := make(map[string]string)
+
+ // 1- if no confFile set, use setting from gdb command file is option
+ // --command/-x is set
+ if confFile == "" && gdbCmdFile != "" {
+ logEarly("Try extract config from gdbCmdFile: %s", gdbCmdFile)
+ confFile, err = extractEnvFromCmdFile(gdbCmdFile)
+ if confFile != "" {
+ defer os.Remove(confFile)
+ }
+ if err != nil {
+ return envMap, confFile, fmt.Errorf(err.Error())
+ }
+ }
+ // 2- search xds-gdb.env file in various locations
+ if confFile == "" {
+ curDir, _ := os.Getwd()
+ if u, err := user.Current(); err == nil {
+ xdsEnvFile := "xds-gdb.env"
+ for _, d := range []string{
+ path.Join(curDir),
+ path.Join(curDir, "..", ".."),
+ path.Join(curDir, "../../target"),
+ path.Join(u.HomeDir, ".xds"),
+ } {
+ confFile := path.Join(d, xdsEnvFile)
+ logEarly("Search config in %s", confFile)
+ if common.Exists(confFile) {
+ break
+ }
+ }
+ }
+ }
+
+ if confFile == "" {
+ return envMap, "", nil
+ }
+
+ if !common.Exists(confFile) {
+ return envMap, confFile, fmt.Errorf("Error no env config file not found")
+ }
+ if err = godotenv.Load(confFile); err != nil {
+ return envMap, confFile, fmt.Errorf("Error loading env config file " + confFile)
+ }
+ if envMap, err = godotenv.Read(confFile); err != nil {
+ return envMap, confFile, fmt.Errorf("Error reading env config file " + confFile)
+ }
+ return envMap, confFile, nil
+}
+
+/*
+ extractEnvFromCmdFile: extract xds-gdb env variable from gdb command file
+ All commented lines (#) in gdb command file that start with ':XDS-ENV:' prefix
+ will be considered as XDS env commands. For example the 3 syntaxes below
+ are supported:
+ # :XDS-ENV: XDS_PROJECT_ID=IW7B4EE-DBY4Z74_myProject
+ #:XDS-ENV:XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot
+ # :XDS-ENV: export XDS_SERVER_URL=localhost:8800
+*/
+func extractEnvFromCmdFile(cmdFile string) (string, error) {
+ if !common.Exists(cmdFile) {
+ return "", nil
+ }
+ cFd, err := os.Open(cmdFile)
+ if err != nil {
+ return "", fmt.Errorf("Cannot open %s : %s", cmdFile, err.Error())
+ }
+ defer cFd.Close()
+
+ var lines []string
+ scanner := bufio.NewScanner(cFd)
+ for scanner.Scan() {
+ lines = append(lines, scanner.Text())
+ }
+ if err = scanner.Err(); err != nil {
+ return "", fmt.Errorf("Cannot parse %s : %s", cmdFile, err.Error())
+ }
+
+ envFile, err := ioutil.TempFile("", "xds-gdb_env.ini")
+ if err != nil {
+ return "", fmt.Errorf("Error while creating temporary env file: %s", err.Error())
+ }
+ envFileName := envFile.Name()
+ defer envFile.Close()
+
+ envFound := false
+ for _, ln := range lines {
+ ln = strings.TrimSpace(ln)
+ if strings.HasPrefix(ln, "#") && strings.Contains(ln, ":XDS-ENV:") {
+ env := strings.SplitAfterN(ln, ":XDS-ENV:", 2)
+ if len(env) == 2 {
+ envFound = true
+ if _, err := envFile.WriteString(strings.TrimSpace(env[1]) + "\n"); err != nil {
+ return "", fmt.Errorf("Error write into temporary env file: %s", err.Error())
+ }
+ } else {
+ log.Warnf("Error while decoding line %s", ln)
+ }
+ }
+ }
+
+ if !envFound {
+ ff := envFileName
+ defer os.Remove(ff)
+ envFileName = ""
+
+ }
+
+ return envFileName, nil
+}
+
+func logEarly(format string, a ...interface{}) {
+ earlyLog = append(earlyLog, fmt.Sprintf(format, a...))
+}