-// 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 (
"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"
)
// 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"
)
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
},
EnvVar{
Name: "XDS_LOGFILE",
- Usage: "logging file",
+ Usage: "logging file (default: " + logFileInitial + ")",
Destination: &logFile,
},
EnvVar{
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))
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 "--":
// 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())
+ 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
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
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)
})
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)}
})
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
// 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 {
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 {
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() {
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())
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())
}
}
}()
// 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
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
}
}
}
if confFile == "" {
+ log.Infof("NO valid conf file found!")
return envMap, "", nil
}
if envMap, err = godotenv.Read(confFile); err != nil {
return envMap, confFile, fmt.Errorf("Error reading env config file " + confFile)
}
+
return envMap, confFile, nil
}
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) {
return envFileName, nil
}
-
-func logEarly(format string, a ...interface{}) {
- earlyLog = append(earlyLog, fmt.Sprintf(format, a...))
-}