Update dependency version for xds agent & common
[src/xds/xds-gdb.git] / main.go
diff --git a/main.go b/main.go
index 1c2e6d8..b193676 100644 (file)
--- a/main.go
+++ b/main.go
@@ -1,4 +1,23 @@
-// xds-gdb: a wrapper on gdb tool for X(cross) Development System.
+/*
+ * Copyright (C) 2017-2018 "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 (
@@ -15,9 +34,9 @@ import (
 
        "path"
 
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
        "github.com/Sirupsen/logrus"
        "github.com/codegangsta/cli"
-       common "github.com/iotbzh/xds-common/golib"
        "github.com/joho/godotenv"
 )
 
@@ -39,11 +58,11 @@ var AppSubVersion = "unknown-dev"
 
 // Create logger
 var log = logrus.New()
-var earlyLog = []string{}
+var logFileInitial = path.Join(os.TempDir(), "xds-gdb.log")
 
 // Application details
 const (
-       appCopyright    = "Apache-2.0"
+       appCopyright    = "Copyright (C) 2017-2018 IoT.bzh - Apache-2.0"
        defaultLogLevel = "warning"
 )
 
@@ -64,16 +83,32 @@ type EnvVar struct {
 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 agentURL, serverURL string
+       var prjID, rPath, logLevel, logFile, sdkid, confFile, gdbNative string
        var listProject bool
        var err error
 
-       uri = "localhost:8000"
+       // 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 {
+               fmt.Printf("WARNING: Cannot create initial log file %s\n", logFileInitial)
+               log.Level = logrus.WarnLevel
+       } else {
+               log.Out = fdL
+               log.Level = logrus.DebugLevel
+       }
+       log.Formatter = &logrus.TextFormatter{}
+
+       agentURL = "localhost:8800"
        logLevel = defaultLogLevel
 
        // Create a new App instance
@@ -109,7 +144,7 @@ func main() {
                },
                EnvVar{
                        Name:        "XDS_LOGFILE",
-                       Usage:       "logging file",
+                       Usage:       "logging file (default: " + logFileInitial + ")",
                        Destination: &logFile,
                },
                EnvVar{
@@ -132,14 +167,20 @@ func main() {
                        Usage:       "Cross Sdk ID to use to build project",
                        Destination: &sdkid,
                },
+               EnvVar{
+                       Name:        "XDS_AGENT_URL",
+                       Usage:       "local XDS agent url",
+                       Destination: &agentURL,
+               },
                EnvVar{
                        Name:        "XDS_SERVER_URL",
-                       Usage:       "remote XDS server url",
-                       Destination: &uri,
+                       Usage:       "overwrite remote XDS server url (default value set in xds-agent-config.json file)",
+                       Destination: &serverURL,
                },
        }
 
        // 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))
@@ -149,7 +190,7 @@ func main() {
        for idx, a := range os.Args[1:] {
                // Specific case to print help or version of xds-gdb
                switch a {
-               case "--help", "-h", "--version", "-v":
+               case "--help", "-h", "--version", "-v", "--list", "-ls":
                        args[1] = a
                        goto endloop
                case "--":
@@ -190,6 +231,7 @@ endloop:
        // 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 {
@@ -229,28 +271,29 @@ endloop:
                var err error
                curDir, _ := os.Getwd()
 
-               // Set logger level, formatter and log file
+               // 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.Formatter = &logrus.TextFormatter{}
+               log.Infof("Switch log level to %s", logLevel)
 
-               // 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
+               if logFile != logFileInitial {
+                       log.Infof("Switch logging to log file %s", logFile)
 
-               // Build env variables
-               env := []string{}
-               for k, v := range envMap {
-                       env = append(env, k+"="+v)
+                       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
@@ -259,18 +302,14 @@ endloop:
                        gdb = NewGdbNative(log, gdbArgs, env)
                } else {
                        gdb = NewGdbXds(log, gdbArgs, env)
-                       gdb.SetConfig("uri", uri)
+                       gdb.SetConfig("agentURL", agentURL)
+                       gdb.SetConfig("serverURL", serverURL)
                        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)
@@ -302,7 +341,13 @@ endloop:
                })
 
                gdb.OnDisconnect(func(err error) {
-                       fmt.Println("Disconnection: ", err.Error())
+                       errMsg := "\nXDS-Agent disconnected"
+                       if err != nil {
+                               fmt.Printf("%s: %v\n", errMsg, err.Error())
+                       } else {
+                               fmt.Println(errMsg)
+                       }
+
                        exitChan <- exitResult{err, int(syscall.ESHUTDOWN)}
                })
 
@@ -312,8 +357,14 @@ endloop:
                                log.Debugf("Recv OUT: <%s>", stdout)
                        }
                        if stderr != "" {
-                               fmt.Fprintf(os.Stderr, "%s", stderr)
-                               log.Debugf("Recv ERR: <%s>", stderr)
+                               // Filter-out ugly message (python error when cross gdb exited)
+                               if !strings.Contains(stderr, "readline.write_history_file") &&
+                                       !(strings.Contains(stderr, "Traceback") && strings.Contains(stderr, "__exithandler")) {
+                                       fmt.Fprintf(os.Stderr, "%s", stderr)
+                                       log.Debugf("Recv ERR: <%s>", stderr)
+                               } else {
+                                       log.Debugf("Recv ERR (FILTERED OUT): <%s>", stderr)
+                               }
                        }
 
                        // Correctly report error about init file
@@ -333,7 +384,7 @@ endloop:
 
                // Handle client tty / pts
                if clientPty != "" {
-                       log.Infoln("Client tty detected: %v\n", clientPty)
+                       log.Infoln("Client tty detected: %v", clientPty)
 
                        cpFd, err := os.OpenFile(clientPty, os.O_RDWR, 0)
                        if err != nil {
@@ -348,7 +399,7 @@ endloop:
                                sc := bufio.NewScanner(reader)
                                for sc.Scan() {
                                        data := sc.Text()
-                                       iosk.Emit(apiv1.ExecInferiorInEvent, data+"\n")
+                                       iosk.Emit(xaapiv1.ExecInferiorInEvent, data+"\n")
                                        log.Debugf("Inferior IN: <%v>", data)
                                }
                                if sc.Err() != nil {
@@ -397,6 +448,10 @@ endloop:
                        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() {
@@ -409,8 +464,18 @@ endloop:
                                                        log.Debugf("OVERWRITE %s -> %s", key, value)
                                                }
                                        }
-                                       gdb.Write(command + "\n")
+
+                                       // 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)
+                                       }
+
                                        log.Debugf("Send: <%v>", command)
+                                       gdb.Write(command + "\n")
                                }
                                log.Infof("Stdin scanner exit, close stdin (err=%v)", sc.Err())
 
@@ -434,8 +499,13 @@ endloop:
                go func() {
                        for {
                                sig := <-sigs
+
+                               if isIgnoredSignal(sig) {
+                                       return
+                               }
+
                                if err := gdb.SendSignal(sig); err != nil {
-                                       log.Errorf("Error while sending signal: %s", err.Error())
+                                       log.Errorf("Error while sending signal %v : %s", sig, err.Error())
                                }
                        }
                }()
@@ -471,13 +541,13 @@ func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]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)
+               log.Infof("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())
+                       log.Infof("Extraction from gdbCmdFile failed: %v", err.Error())
                }
        }
        // 2- search xds-gdb.env file in various locations
@@ -487,13 +557,14 @@ func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string,
                        xdsEnvFile := "xds-gdb.env"
                        for _, d := range []string{
                                path.Join(curDir),
-                               path.Join(curDir, "..", ".."),
-                               path.Join(curDir, "../../target"),
-                               path.Join(u.HomeDir, ".xds"),
+                               path.Join(curDir, ".."),
+                               path.Join(curDir, "target"),
+                               path.Join(u.HomeDir, ".config", "xds"),
                        } {
-                               confFile = path.Join(d, xdsEnvFile)
-                               logEarly("Search config in %s", confFile)
-                               if common.Exists(confFile) {
+                               cf := path.Join(d, xdsEnvFile)
+                               log.Infof("Search config in %s", cf)
+                               if common.Exists(cf) {
+                                       confFile = cf
                                        break
                                }
                        }
@@ -501,6 +572,7 @@ func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string,
        }
 
        if confFile == "" {
+               log.Infof("NO valid conf file found!")
                return envMap, "", nil
        }
 
@@ -513,6 +585,7 @@ func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string,
        if envMap, err = godotenv.Read(confFile); err != nil {
                return envMap, confFile, fmt.Errorf("Error reading env config file " + confFile)
        }
+
        return envMap, confFile, nil
 }
 
@@ -521,9 +594,9 @@ func loadConfigEnvFile(confFile, gdbCmdFile string) (map[string]string, string,
   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
+  # :XDS-ENV: XDS_PROJECT_ID=4021617e-ced0-11e7-acd2-3c970e49ad9b
+  #:XDS-ENV:XDS_SDK_ID=06c0e95a-e215-3a5a-b373-f677c0dabd3b
+  # :XDS-ENV:  export XDS_AGENT_URL=localhost:8800
 */
 func extractEnvFromCmdFile(cmdFile string) (string, error) {
        if !common.Exists(cmdFile) {
@@ -576,7 +649,3 @@ func extractEnvFromCmdFile(cmdFile string) (string, error) {
 
        return envFileName, nil
 }
-
-func logEarly(format string, a ...interface{}) {
-       earlyLog = append(earlyLog, fmt.Sprintf(format, a...))
-}