summaryrefslogtreecommitdiffstats
path: root/main.go
blob: 81b9154a38260d09cad6912a6146173287bbbfac (plain)
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

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
<?xml version="1.0" encoding="UTF-8"?>
<manifest>

  <remote name="agl"
         fetch="https://gerrit.automotivelinux.org/gerrit/"
         review="https://gerrit.automotivelinux.org/gerrit/"
         pushurl="ssh://gerrit.automotivelinux.org:29418"
  />

  <remote name="yocto" fetch="git://git.yoctoproject.org/" />
  <remote name="openembedded" fetch="git://git.openembedded.org/" />

  <!-- meta-updater --> <!-- freescale bsp --> <!-- 01.org -->
  <remote name="github" fetch="https://github.com/" />

  <!-- meta-qt5  -->
  <remote name="qt.io" fetch="git://code.qt.io/" />

  <default remote="agl" sync-j="4" revision="refs/tags/guppy/6.99.1"/>

  <!-- AGL things. -->
  <project name="AGL/meta-agl" path="meta-agl" />
  <project name="AGL/meta-agl-demo" path="meta-agl-demo" />
  <project name="AGL/meta-agl-devel" path="meta-agl-devel" />
  <project name="AGL/meta-agl-extra" path="meta-agl-extra" />

  <!-- Renesas Gen3 specific things -->
  <project name="AGL/meta-renesas-rcar-gen3" path="meta-renesas-rcar-gen3"/>
  <project name="CogentEmbedded/meta-rcar" path="meta-rcar" remote="github" revision="de24bb576c135c6617827d977382611d894ffb70" upstream="v3.9.0"/>

  <!-- Updater layers. -->
  <project name="advancedtelematic/meta-updater" path="meta-updater" remote="github" revision="ff555e8690eb47177ade42dc6912ae17a759cc45" upstream="rocko"/>
  <project name="advancedtelematic/meta-updater-qemux86-64" path="meta-updater-qemux86-64" remote="github" revision="697632ddd98ed7ae3dbd0bd84abb04079767bc56" upstream="rocko"/>

  <!-- Yocto/OpenEmbedded things. -->
  <project name="poky" path="poky" remote="yocto" revision="05711ba18587aaaf4a9c465a1dd4537f27ceda93" upstream="rocko" />
  <project name="meta-gplv2" path="meta-gplv2" remote="yocto" revision="f875c60ecd6f30793b80a431a2423c4b98e51548" upstream="rocko" />
  <project name="meta-openembedded" path="meta-openembedded" remote="openembedded" revision="eae996301d9c097bcbeb8046f08041dc82bb62f8" upstream="rocko" />
  <project name="meta-virtualization" path="meta-virtualization" remote="yocto" revision="bd77388f31929f38e7d4cc9c711f0f83f563007e" upstream="rocko"/>

  <!-- Qt things -->
  <!-- Qt 5.8 -->
  <project name="meta-qt5/meta-qt5" path="meta-qt5" remote="github" revision="682ad61c071a9710e9f9d8a32ab1b5f3c14953d1" upstream="rocko"/>

  <!-- MinnowBoard MAX specific things -->
  <project name="meta-intel" path="meta-intel" remote="yocto" revision="718bb384942675437c081f6795da7f421da1fee6" upstream="rocko"/>

  <!-- i.MX6 / e.g. wandboard specific things -->
  <!--
  <project name="Freescale/meta-freescale" path="meta-freescale" remote="github" revision="06178400afbd641a6709473fd21d893dcd3cfbfa" upstream="pyro"/>
  <project name="Freescale/meta-freescale-3rdparty" path="meta-freescale-3rdparty" remote="github" revision="35badbde05d4f10d4faeefc30bc126b5bd228e2e" upstream="pyro"/>
  <project name="Freescale/meta-freescale-distro" path="meta-freescale-distro" remote="github" revision="cd5c7a2539f40004f74126e9fdf08254fd9a6390" upstream="pyro"/>
  -->
  <!-- consolida
/*
 * Copyright (C) 2017 "IoT.bzh"
 * Author Sebastien Douheret <sebastien@iot.bzh>
 *
 * 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.
 *
 *
 * 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 logFileInitial = "/tmp/xds-gdb.log"

// Application details
const (
	appCopyright    = "Copyright (C) 2017 IoT.bzh - 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")
	log.Debugf("Exit: code=%v, err=%s", code, err)

	os.Exit(int(code))
}

// main
func main() {
	var uri, prjID, rPath, logLevel, logFile, sdkid, confFile, gdbNative string
	var listProject bool
	var err error

	// Init Logger and set temporary file and level for the 1st part
	// IOW while XDS_LOGLEVEL and XDS_LOGFILE options are not parsed
	logFile = logFileInitial
	fdL, err := os.OpenFile(logFileInitial, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if err != nil {
		msgErr := fmt.Sprintf("Cannot create log file %s", logFileInitial)
		exitError(syscall.EPERM, msgErr)
	}
	log.Formatter = &logrus.TextFormatter{}
	log.Out = fdL
	log.Level = logrus.DebugLevel

	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 (default: " + logFileInitial + ")",
			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
	log.Debugf("xds-gdb started with args: %v", os.Args)
	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", "--list", "-ls":
			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)
	log.Infof("Load env config: envMap=%v, confFile=%v, err=%v", envMap, confFile, err)

	// Only rise an error when args is not set (IOW when --help or --version is not set)
	if len(args) == 1 {
		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()

		// Build env variables
		env := []string{}
		for k, v := range envMap {
			env = append(env, k+"="+v)
		}

		// Now set logger level and log file to correct/env var settings
		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.Infof("Switch log level to %s", logLevel)

		if logFile != logFileInitial {
			log.Infof("Switch logging to log file %s", logFile)

			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))
			}
			defer fdL.Close()
			log.Out = fdL
		}

		// 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 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", 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(xaapiv1.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)

			// Enable workaround to correctly close connection
			// except if XDS_GDBSERVER_EXIT_NOFIX is defined
			_, gdbExitNoFix := os.LookupEnv("XDS_GDBSERVER_EXIT_NOFIX")

			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)
						}
					}

					// Send SIGINT to stop debugged process execution before sending -gdb-exit command
					if !gdbExitNoFix && strings.Contains(command, "-gdb-exit") {
						log.Infof("Detection of -gdb-exit, exiting...")
						if err := gdb.SendSignal(syscall.SIGINT); err != nil {
							log.Errorf("Error while sending signal SIGINT : %s", err.Error())
						}
						time.Sleep(time.Millisecond * 200)
					}

					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 isIgnoredSignal(sig) {
					return
				}

				if err := gdb.SendSignal(sig); err != nil {
					log.Errorf("Error while sending signal %v : %s", sig, 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 != "" {
		log.Infof("Try extract config from gdbCmdFile: %s", gdbCmdFile)
		confFile, err = extractEnvFromCmdFile(gdbCmdFile)
		if confFile != "" {
			defer os.Remove(confFile)
		}
		if err != nil {
			log.Infof("Extraction from gdbCmdFile failed: %v", 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, ".config", "xds"),
			} {
				cf := path.Join(d, xdsEnvFile)
				log.Infof("Search config in %s", cf)
				if common.Exists(cf) {
					confFile = cf
					break
				}
			}
		}
	}

	if confFile == "" {
		log.Infof("NO valid conf file found!")
		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
}