X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fxds%2Fxds-gdb.git;a=blobdiff_plain;f=gdb-xds.go;h=9dfb71711199981da0058a4bf53615a91c8fa6ed;hp=22e13ed41d1bfe36610165c229a3d41fa61ea665;hb=f95f219c20ec7d67b11c011dff84da74a7fd63de;hpb=662dd0f910150228ba93096d77637f697c2c3ed7 diff --git a/gdb-xds.go b/gdb-xds.go index 22e13ed..9dfb717 100644 --- a/gdb-xds.go +++ b/gdb-xds.go @@ -1,38 +1,59 @@ +/* + * Copyright (C) 2017-2018 "IoT.bzh" + * Author Sebastien Douheret + * + * 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. + * + */ + package main import ( "encoding/json" "fmt" - "net/http" "os" + "regexp" + "runtime" + "strconv" "strings" "syscall" + "text/tabwriter" + "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1" + common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib" "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/folder" - sio_client "github.com/zhouhui8915/go-socket.io-client" + sio_client "github.com/sebd71/go-socket.io-client" ) -// GdbXds - +// GdbXds - Implementation of IGDB used to interfacing XDS type GdbXds struct { - log *logrus.Logger - ccmd string - aargs []string - eenv []string - uri string - prjID string - sdkID string - rPath string - listPrj bool - cmdID string + log *logrus.Logger + ccmd string + aargs []string + eenv []string + agentURL string + serverURL string + prjID string + sdkID string + rPath string + listPrj bool + cmdID string + xGdbPid string httpCli *common.HTTPClient ioSock *sio_client.Client - folders []folder.FolderConfig + projects []xaapiv1.ProjectConfig // callbacks cbOnError func(error) @@ -51,20 +72,27 @@ func NewGdbXds(log *logrus.Logger, args []string, env []string) *GdbXds { eenv: env, httpCli: nil, ioSock: nil, + xGdbPid: strconv.Itoa(os.Getpid()), } } // SetConfig set additional config fields func (g *GdbXds) SetConfig(name string, value interface{}) error { + var val string + if name != "listProject" { + val = strings.TrimSpace(value.(string)) + } switch name { - case "uri": - g.uri = value.(string) + case "agentURL": + g.agentURL = val + case "serverURL": + g.serverURL = val case "prjID": - g.prjID = value.(string) + g.prjID = val case "sdkID": - g.sdkID = value.(string) + g.sdkID = val case "rPath": - g.rPath = value.(string) + g.rPath = val case "listProject": g.listPrj = value.(bool) default: @@ -80,34 +108,85 @@ func (g *GdbXds) Init() (int, error) { g.cmdID = "" // Define HTTP and WS url - baseURL := g.uri - if !strings.HasPrefix(g.uri, "http://") { - baseURL = "http://" + g.uri + baseURL := g.agentURL + + // Allow to only set port number + if match, _ := regexp.MatchString("^([0-9]+)$", baseURL); match { + baseURL = "http://localhost:" + g.agentURL + } + // Add http prefix if missing + if baseURL != "" && !strings.HasPrefix(g.agentURL, "http://") { + baseURL = "http://" + g.agentURL } // Create HTTP client g.log.Infoln("Connect HTTP client on ", baseURL) conf := common.HTTPClientConfig{ URLPrefix: "/api/v1", - HeaderClientKeyName: "XDS-SID", + HeaderClientKeyName: "Xds-Agent-Sid", CsrfDisable: true, + LogOut: g.log.Out, + LogPrefix: "XDSAGENT: ", + LogLevel: common.HTTPLogLevelDebug, } c, err := common.HTTPNewClient(baseURL, conf) if err != nil { - return int(syscallEBADE), err + errmsg := err.Error() + m, err := regexp.MatchString("Get http.?://", errmsg) + if (m && err == nil) || strings.Contains(errmsg, "Failed to get device ID") { + i := strings.LastIndex(errmsg, ":") + newErr := "Cannot connection to " + baseURL + if i > 0 { + newErr += " (" + strings.TrimSpace(errmsg[i+1:]) + ")" + } else { + newErr += " (" + strings.TrimSpace(errmsg) + ")" + } + errmsg = newErr + } + return int(syscallEBADE), fmt.Errorf(errmsg) } g.httpCli = c + g.httpCli.SetLogLevel(g.log.Level.String()) + g.log.Infoln("HTTP session ID:", g.httpCli.GetClientID()) - // First call to check that xds-server is alive + // First call to check that xds-agent and server are alive + ver := xaapiv1.XDSVersion{} + if err := g.httpCli.Get("/version", &ver); err != nil { + return int(syscallEBADE), err + } + g.log.Infoln("XDS agent & server version:", ver) + + // Get current config and update connection to server when needed + xdsConf := xaapiv1.APIConfig{} + if err := g.httpCli.Get("/config", &xdsConf); err != nil { + return int(syscallEBADE), err + } + // FIXME: add multi-servers support + idx := 0 + svrCfg := xdsConf.Servers[idx] + if g.serverURL != "" && (svrCfg.URL != g.serverURL || !svrCfg.Connected) { + svrCfg.URL = g.serverURL + svrCfg.ConnRetry = 10 + newCfg := xaapiv1.APIConfig{} + if err := g.httpCli.Post("/config", xdsConf, &newCfg); err != nil { + return int(syscallEBADE), err + } + + } else if !svrCfg.Connected { + return int(syscallEBADE), fmt.Errorf("XDS server not connected (url=%s)", svrCfg.URL) + } + + // Get XDS projects list var data []byte - if err := c.HTTPGet("/folders", &data); err != nil { + if err := g.httpCli.HTTPGet("/projects", &data); err != nil { return int(syscallEBADE), err } - g.log.Infof("Result of /folders: %v", string(data[:])) - g.folders = []folder.FolderConfig{} - errMar := json.Unmarshal(data, &g.folders) + + g.log.Infof("Result of /projects: %v", string(data[:])) + g.projects = []xaapiv1.ProjectConfig{} + errMar := json.Unmarshal(data, &g.projects) if errMar != nil { - g.log.Errorf("Cannot decode folders configuration: %s", errMar.Error()) + g.log.Errorf("Cannot decode projects configuration: %s", errMar.Error()) } // Check mandatory args @@ -122,7 +201,7 @@ func (g *GdbXds) Init() (int, error) { Transport: "websocket", Header: make(map[string][]string), } - opts.Header["XDS-SID"] = []string{c.GetClientID()} + opts.Header["XDS-AGENT-SID"] = []string{c.GetClientID()} iosk, err := sio_client.NewClient(baseURL, opts) if err != nil { @@ -143,27 +222,67 @@ func (g *GdbXds) Init() (int, error) { } }) - iosk.On(apiv1.ExecOutEvent, func(ev apiv1.ExecOutMsg) { + // SEB gdbPid := "" + iosk.On(xaapiv1.ExecOutEvent, func(ev xaapiv1.ExecOutMsg) { if g.cbRead != nil { g.cbRead(ev.Timestamp, ev.Stdout, ev.Stderr) + /* + stdout := ev.Stdout + // SEB + //New Thread 15139 + if strings.Contains(stdout, "pid = ") { + re := regexp.MustCompile("pid = ([0-9]+)") + if res := re.FindAllStringSubmatch(stdout, -1); len(res) > 0 { + gdbPid = res[0][1] + } + g.log.Errorf("SEB FOUND THREAD in '%s' => gdbPid=%s", stdout, gdbPid) + } + if gdbPid != "" && g.xGdbPid != "" && strings.Contains(stdout, gdbPid) { + g.log.Errorf("SEB THREAD REPLACE 1 stdout=%s", stdout) + stdout = strings.Replace(stdout, gdbPid, g.xGdbPid, -1) + g.log.Errorf("SEB THREAD REPLACE 2 stdout=%s", stdout) + } + + g.cbRead(ev.Timestamp, stdout, ev.Stderr) + */ } }) - iosk.On(apiv1.ExecInferiorOutEvent, func(ev apiv1.ExecOutMsg) { + iosk.On(xaapiv1.ExecInferiorOutEvent, func(ev xaapiv1.ExecOutMsg) { if g.cbInferiorRead != nil { g.cbInferiorRead(ev.Timestamp, ev.Stdout, ev.Stderr) } }) - iosk.On(apiv1.ExecExitEvent, func(ev apiv1.ExecExitMsg) { + iosk.On(xaapiv1.ExecExitEvent, func(ev xaapiv1.ExecExitMsg) { if g.cbOnExit != nil { g.cbOnExit(ev.Code, ev.Error) } }) + // Monitor XDS server configuration changes (and specifically connected status) + iosk.On(xaapiv1.EVTServerConfig, func(ev xaapiv1.EventMsg) { + svrCfg, err := ev.DecodeServerCfg() + if err == nil && !svrCfg.Connected { + // TODO: should wait that server will be connected back + if g.cbOnExit != nil { + g.cbOnExit(-1, fmt.Errorf("XDS Server disconnected")) + } else { + fmt.Printf("XDS Server disconnected") + os.Exit(-1) + } + } + }) + + args := xaapiv1.EventRegisterArgs{Name: xaapiv1.EVTServerConfig} + if err := g.httpCli.Post("/events/register", args, nil); err != nil { + return 0, err + } + return 0, nil } +// Close frees allocated objects and close opened connections func (g *GdbXds) Close() error { g.cbOnDisconnect = nil g.cbOnError = nil @@ -177,23 +296,23 @@ func (g *GdbXds) Close() error { // 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 - var folder *folder.FolderConfig + var project *xaapiv1.ProjectConfig - // Retrieve the folder definition - for _, f := range g.folders { - if f.ID == g.prjID { - folder = &f + // Retrieve the project definition + for _, f := range g.projects { + // check as prefix to support short/partial id name + if strings.HasPrefix(f.ID, g.prjID) { + project = &f break } } // Auto setup rPath if needed - if g.rPath == "" && folder != nil { + if g.rPath == "" && project != nil { cwd, err := os.Getwd() if err == nil { - fldRp := folder.ClientPath + fldRp := project.ClientPath if !strings.HasPrefix(fldRp, "/") { fldRp = "/" + fldRp } @@ -209,7 +328,12 @@ func (g *GdbXds) Start(inferiorTTY bool) (int, error) { // except if XDS_GDBSERVER_OUTPUT_NOFIX is defined _, gdbserverNoFix := os.LookupEnv("XDS_GDBSERVER_OUTPUT_NOFIX") - args := apiv1.ExecArgs{ + // SDK ID must be set else $GDB cannot be resolved + if g.sdkID == "" { + return int(syscall.EINVAL), fmt.Errorf("sdkid must be set") + } + + args := xaapiv1.ExecArgs{ ID: g.prjID, SdkID: g.sdkID, Cmd: g.ccmd, @@ -220,24 +344,17 @@ func (g *GdbXds) Start(inferiorTTY bool) (int, error) { 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)) + g.log.Infof("POST %s/exec %v", g.agentURL, args) + res := xaapiv1.ExecResult{} + err = g.httpCli.Post("/exec", args, &res) 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 + if res.CmdID == "" { + return int(syscallEBADE), fmt.Errorf("null CmdID") } - g.cmdID = dRes["cmdID"].(string) + g.cmdID = res.CmdID return 0, nil } @@ -284,7 +401,7 @@ func (g *GdbXds) InferiorRead(f func(timestamp, stdout, stderr string)) { // Write writes message/string into gdb stdin func (g *GdbXds) Write(args ...interface{}) error { - return g.ioSock.Emit(apiv1.ExecInEvent, args...) + return g.ioSock.Emit(xaapiv1.ExecInEvent, args...) } // SendSignal is used to send a signal to remote process/gdb @@ -293,56 +410,55 @@ func (g *GdbXds) SendSignal(sig os.Signal) error { return fmt.Errorf("cmdID not set") } - var body []byte - body, err := json.Marshal(apiv1.ExecSignalArgs{ + sigArg := xaapiv1.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)) + g.log.Debugf("POST /signal %v", sigArg) + return g.httpCli.Post("/signal", sigArg, nil) } //***** Private functions ***** func (g *GdbXds) printProjectsList() (int, error) { + writer := new(tabwriter.Writer) + writer.Init(os.Stdout, 0, 8, 0, '\t', 0) msg := "" - if len(g.folders) > 0 { - msg += "List of existing projects (use: export XDS_PROJECT_ID=<< ID >>): \n" - msg += " ID\t\t\t\t | Label" - for _, f := range g.folders { - msg += fmt.Sprintf("\n %s\t | %s", f.ID, f.Label) - if f.DefaultSdk != "" { - msg += fmt.Sprintf("\t(default SDK: %s)", f.DefaultSdk) - } + if len(g.projects) > 0 { + fmt.Fprintln(writer, "List of existing projects (use: export XDS_PROJECT_ID=<< ID >>):") + fmt.Fprintln(writer, "ID \t Label") + for _, f := range g.projects { + fmt.Fprintf(writer, " %s \t %s\n", f.ID, f.Label) } - msg += "\n" } - var data []byte - if err := g.httpCli.HTTPGet("/sdks", &data); err != nil { + // FIXME : support multiple servers + sdks := []xaapiv1.SDK{} + if err := g.httpCli.Get("/servers/0/sdks", &sdks); err != nil { return int(syscallEBADE), err } - g.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) + fmt.Fprintln(writer, "\nList of installed cross SDKs (use: export XDS_SDK_ID=<< ID >>):") + fmt.Fprintln(writer, "ID \t Name") + for _, s := range sdks { + if s.Status == xaapiv1.SdkStatusInstalled { + fmt.Fprintf(writer, " %s \t %s\n", s.ID, s.Name) } } - if len(g.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", - g.folders[0].ID, sdks[0].ID, AppName) + if len(g.projects) > 0 && len(sdks) > 0 { + fmt.Fprintln(writer, "") + fmt.Fprintln(writer, "For example: ") + if runtime.GOOS == "windows" { + fmt.Fprintf(writer, " SET XDS_PROJECT_ID=%s && SET XDS_SDK_ID=%s && %s -x myGdbConf.ini\n", + g.projects[0].ID[:8], sdks[0].ID[:8], AppName) + } else { + fmt.Fprintf(writer, " XDS_PROJECT_ID=%s XDS_SDK_ID=%s %s -x myGdbConf.ini\n", + g.projects[0].ID[:8], sdks[0].ID[:8], AppName) + } } + fmt.Fprintln(writer, "") + fmt.Fprintln(writer, "Or define settings within gdb configuration file (see help and :XDS-ENV: tag)") + writer.Flush() return 0, fmt.Errorf(msg) }