func main() { if bundleDir == "" { log.Fatal("Please set the GOPATH that includes the godbg project and re-run.") return } if flag.NArg() < 1 { flag.Usage() return } execPath := flag.Arg(0) // Check to see if the executable path is really a go package that // exists in the gopath's source directory if !filepath.IsAbs(execPath) { pkgPath := execPath pkgSrcDir := "" pkgBase := filepath.Base(pkgPath) for _, path := range gopaths { srcPathMatch := filepath.Join(path, "src", pkgPath) binPathMatch := filepath.Join(path, "bin", pkgBase) // Try to discover the src location and delete any existing binaries // for this packages _, err := os.Stat(srcPathMatch) if err == nil { pkgSrcDir = srcPathMatch if *srcDir == "" { srcDir = &pkgSrcDir } _, err = os.Stat(binPathMatch) execPath = binPathMatch if err == nil { os.Remove(execPath) execFile, _ := os.Open(execPath) if execFile != nil { _, err := execFile.Stat() if err == nil { fmt.Fprintf(os.Stderr, "Could not clean existing binary in order to recompile with debug flags. %v\n", execPath) os.Exit(1) } } } } // Delete the "pkg" directory with any lingering archives os.RemoveAll(filepath.Join(path, "pkg")) } cmd := exec.Command("go", "install", "-gcflags", "-N -l", pkgPath) msg, err := cmd.CombinedOutput() if err != nil { fmt.Printf("Could not compile binary with debug flags: %v\n%v\n", pkgPath, string(msg)) os.Exit(1) } } mygdb, err := gdblib.NewGDB(execPath, *srcDir) if err != nil { panic(err) } serverAddrChan := make(chan string) go func() { file, _ := os.Open(bundleDir) bundleNames, _ := file.Readdirnames(-1) bundleFileSystems := make([]http.FileSystem, len(bundleNames), len(bundleNames)) for idx, bundleName := range bundleNames { bundleFileSystems[idx] = http.Dir(filepath.Join(bundleDir, bundleName, "web")) } cfs := chainedFileSystem{fs: bundleFileSystems} http.HandleFunc("/", wrapFileServer(http.FileServer(cfs))) http.HandleFunc("/output", wrapWebSocket(websocket.Handler(func(ws *websocket.Conn) { type webSockResult struct { Type string Data interface{} } for { select { case data := <-mygdb.Console: bytes, err := json.Marshal(&webSockResult{Type: "console", Data: data}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case data := <-mygdb.Target: bytes, err := json.Marshal(&webSockResult{Type: "target", Data: data}) if len(data) > 9 && data[:9] == "[stderr] " { fmt.Fprintf(os.Stderr, data[9:]) } else { fmt.Fprintf(os.Stdout, data) } if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case data := <-mygdb.InternalLog: bytes, err := json.Marshal(&webSockResult{Type: "gdb", Data: data}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case record := <-mygdb.AsyncResults: bytes, err := json.Marshal(&webSockResult{Type: "async", Data: record}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case <-time.After(30 * time.Second): // Send heartbeat and disconnect if client doesn't receive it bytes, err := json.Marshal(&webSockResult{Type: "heartbeat", Data: ""}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error } } }))) // Add handlers for each category of gdb commands (exec, breakpoint, thread, etc.) addExecHandlers(mygdb) addBreakpointHandlers(mygdb) addThreadHandlers(mygdb) addFrameHandlers(mygdb) addVariableHandlers(mygdb) http.HandleFunc("/handle/gdb/exit", wrapHandlerFunc(func(w http.ResponseWriter, r *http.Request) { mygdb.GdbExit() })) // Unsecure local connection through the loopback interface if hostName == loopbackHost { listener, err := net.Listen("tcp", hostName+":0") if err != nil { panic(err) } serverAddrChan <- listener.Addr().String() http.Serve(listener, nil) } else { // Secure connection requires a SSL/TLS cerificate and key config := &tls.Config{} if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { panic(err) } listener, err := tls.Listen("tcp", hostName+":0", config) if err != nil { panic(err) } serverAddrChan <- strings.Replace(listener.Addr().String(), loopbackHost, hostName, 1) http.Serve(listener, nil) } }() go func() { serverAddr := <-serverAddrChan url := "" if hostName != loopbackHost { url = "https://" + serverAddr + "/?MAGIC=" + magicKey } else { url = "http://" + serverAddr } if *autoOpen { openBrowser(url) } else { fmt.Printf("%v\n", url) } }() execArgs := flag.Args()[1:] mygdb.ExecArgs(gdblib.ExecArgsParms{strings.Join(execArgs, " ")}) mygdb.ExecRun(gdblib.ExecRunParms{}) err = mygdb.Wait() if err != nil { panic(err) } }
func main() { if bundleDir == "" { fmt.Fprintf(os.Stderr, "Please set the GOPATH to include the godbg project and re-run.\n") return } if flag.NArg() < 1 { flag.Usage() return } execPath := flag.Arg(0) // Check to see if the executable path is really a go package that // exists in the gopath's source directory if !filepath.IsAbs(execPath) { pkgPath := execPath pkgSrcDir := "" pkgBase := filepath.Base(pkgPath) for _, path := range gopaths { srcPathMatch := filepath.Join(path, "src", pkgPath) binPathMatch := filepath.Join(path, "bin", pkgBase) _, err := os.Stat(srcPathMatch) if err == nil { pkgSrcDir = srcPathMatch if *srcDir == "" { srcDir = &pkgSrcDir } _, err = os.Stat(binPathMatch) execPath = binPathMatch if err == nil { os.Remove(execPath) execFile, _ := os.Open(execPath) if execFile != nil { _, err := execFile.Stat() if err == nil { fmt.Fprintf(os.Stderr, "Could not clean existing binary in order to recompile with debug flags. %v\n", execPath) os.Exit(1) } } } cmd := exec.Command("go", "install", "-gcflags", "-N -l", pkgPath) msg, err := cmd.CombinedOutput() if err != nil { fmt.Printf("Could not compile binary with debug flags: %v\n%v\n", pkgPath, string(msg)) os.Exit(1) } } } } mygdb, err := gdblib.NewGDB(execPath, *srcDir) if err != nil { panic(err) } serverAddrChan := make(chan string) go func() { file, _ := os.Open(bundleDir) bundleNames, _ := file.Readdirnames(-1) bundleFileSystems := make([]http.FileSystem, len(bundleNames), len(bundleNames)) for idx, bundleName := range bundleNames { bundleFileSystems[idx] = http.Dir(filepath.Join(bundleDir, bundleName, "web")) } cfs := chainedFileSystem{fs: bundleFileSystems} http.Handle("/", http.FileServer(cfs)) http.Handle("/output", websocket.Handler(func(ws *websocket.Conn) { type webSockResult struct { Type string Data interface{} } for { select { case data := <-mygdb.Console: bytes, err := json.Marshal(&webSockResult{Type: "console", Data: data}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case data := <-mygdb.Target: bytes, err := json.Marshal(&webSockResult{Type: "target", Data: data}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case data := <-mygdb.InternalLog: bytes, err := json.Marshal(&webSockResult{Type: "gdb", Data: data}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case record := <-mygdb.AsyncResults: bytes, err := json.Marshal(&webSockResult{Type: "async", Data: record}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error case <-time.After(30 * time.Second): // Send heartbeat and disconnect if client doesn't receive it bytes, err := json.Marshal(&webSockResult{Type: "heartbeat", Data: ""}) if err == nil { _, err := ws.Write(bytes) if err != nil { fmt.Printf("Client disconnect\n") mygdb.GdbExit() } } // TODO log the marshalling error } } })) // Add handlers for each category of gdb commands (exec, breakpoint, thread, etc.) addExecHandlers(mygdb) addBreakpointHandlers(mygdb) addThreadHandlers(mygdb) addFrameHandlers(mygdb) addVariableHandlers(mygdb) http.HandleFunc("/handle/gdb/exit", func(w http.ResponseWriter, r *http.Request) { mygdb.GdbExit() }) listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { panic(err) } serverAddrChan <- listener.Addr().String() http.Serve(listener, nil) }() go func() { serverAddr := <-serverAddrChan if *autoOpen { openBrowser("http://" + serverAddr) } else { fmt.Printf("http://%v\n", serverAddr) } }() execArgs := flag.Args()[1:] mygdb.ExecArgs(gdblib.ExecArgsParms{strings.Join(execArgs, " ")}) mygdb.ExecRun(gdblib.ExecRunParms{}) err = mygdb.Wait() if err != nil { panic(err) } }