Example #1
0
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)
	}
}
Example #2
0
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)
	}
}