Index: src/pkg/exp/ogle/cmd.go |
=================================================================== |
deleted file mode 100644 |
--- a/src/pkg/exp/ogle/cmd.go |
+++ /dev/null |
@@ -1,373 +0,0 @@ |
-// Copyright 2009 The Go Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style |
-// license that can be found in the LICENSE file. |
- |
-// Package ogle is the beginning of a debugger for Go. |
-package ogle |
- |
-import ( |
- "bufio" |
- "debug/elf" |
- "debug/proc" |
- "exp/eval" |
- "fmt" |
- "go/scanner" |
- "go/token" |
- "os" |
- "strconv" |
- "strings" |
-) |
- |
-var fset = token.NewFileSet() |
-var world *eval.World |
-var curProc *Process |
- |
-func Main() { |
- world = eval.NewWorld() |
- defineFuncs() |
- r := bufio.NewReader(os.Stdin) |
- for { |
- print("; ") |
- line, err := r.ReadSlice('\n') |
- if err != nil { |
- break |
- } |
- |
- // Try line as a command |
- cmd, rest := getCmd(line) |
- if cmd != nil { |
- err := cmd.handler(rest) |
- if err != nil { |
- scanner.PrintError(os.Stderr, err) |
- } |
- continue |
- } |
- |
- // Try line as code |
- code, err := world.Compile(fset, string(line)) |
- if err != nil { |
- scanner.PrintError(os.Stderr, err) |
- continue |
- } |
- v, err := code.Run() |
- if err != nil { |
- fmt.Fprintf(os.Stderr, err.String()) |
- continue |
- } |
- if v != nil { |
- println(v.String()) |
- } |
- } |
-} |
- |
-// newScanner creates a new scanner that scans that given input bytes. |
-func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) { |
- sc := new(scanner.Scanner) |
- ev := new(scanner.ErrorVector) |
- file := fset.AddFile("input", fset.Base(), len(input)) |
- sc.Init(file, input, ev, 0) |
- return sc, ev |
-} |
- |
-/* |
- * Commands |
- */ |
- |
-// A UsageError occurs when a command is called with illegal arguments. |
-type UsageError string |
- |
-func (e UsageError) String() string { return string(e) } |
- |
-// A cmd represents a single command with a handler. |
-type cmd struct { |
- cmd string |
- handler func([]byte) os.Error |
-} |
- |
-var cmds = []cmd{ |
- {"load", cmdLoad}, |
- {"bt", cmdBt}, |
-} |
- |
-// getCmd attempts to parse an input line as a registered command. If |
-// successful, it returns the command and the bytes remaining after |
-// the command, which should be passed to the command. |
-func getCmd(line []byte) (*cmd, []byte) { |
- sc, _ := newScanner(line) |
- pos, tok, lit := sc.Scan() |
- if sc.ErrorCount != 0 || tok != token.IDENT { |
- return nil, nil |
- } |
- |
- slit := string(lit) |
- for i := range cmds { |
- if cmds[i].cmd == slit { |
- return &cmds[i], line[fset.Position(pos).Offset+len(lit):] |
- } |
- } |
- return nil, nil |
-} |
- |
-// cmdLoad starts or attaches to a process. Its form is similar to |
-// import: |
-// |
-// load [sym] "path" [;] |
-// |
-// sym specifies the name to give to the process. If not given, the |
-// name is derived from the path of the process. If ".", then the |
-// packages from the remote process are defined into the current |
-// namespace. If given, this symbol is defined as a package |
-// containing the process' packages. |
-// |
-// path gives the path of the process to start or attach to. If it is |
-// "pid:<num>", then attach to the given PID. Otherwise, treat it as |
-// a file path and space-separated arguments and start a new process. |
-// |
-// load always sets the current process to the loaded process. |
-func cmdLoad(args []byte) os.Error { |
- ident, path, err := parseLoad(args) |
- if err != nil { |
- return err |
- } |
- if curProc != nil { |
- return UsageError("multiple processes not implemented") |
- } |
- if ident != "." { |
- return UsageError("process identifiers not implemented") |
- } |
- |
- // Parse argument and start or attach to process |
- var fname string |
- var tproc proc.Process |
- if len(path) >= 4 && path[0:4] == "pid:" { |
- pid, err := strconv.Atoi(path[4:]) |
- if err != nil { |
- return err |
- } |
- fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid)) |
- if err != nil { |
- return err |
- } |
- tproc, err = proc.Attach(pid) |
- if err != nil { |
- return err |
- } |
- println("Attached to", pid) |
- } else { |
- parts := strings.Split(path, " ") |
- if len(parts) == 0 { |
- fname = "" |
- } else { |
- fname = parts[0] |
- } |
- tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}}) |
- if err != nil { |
- return err |
- } |
- println("Started", path) |
- // TODO(austin) If we fail after this point, kill tproc |
- // before detaching. |
- } |
- |
- // Get symbols |
- f, err := os.Open(fname) |
- if err != nil { |
- tproc.Detach() |
- return err |
- } |
- defer f.Close() |
- elf, err := elf.NewFile(f) |
- if err != nil { |
- tproc.Detach() |
- return err |
- } |
- curProc, err = NewProcessElf(tproc, elf) |
- if err != nil { |
- tproc.Detach() |
- return err |
- } |
- |
- // Prepare new process |
- curProc.OnGoroutineCreate().AddHandler(EventPrint) |
- curProc.OnGoroutineExit().AddHandler(EventPrint) |
- |
- err = curProc.populateWorld(world) |
- if err != nil { |
- tproc.Detach() |
- return err |
- } |
- |
- return nil |
-} |
- |
-func parseLoad(args []byte) (ident string, path string, err os.Error) { |
- err = UsageError("Usage: load [sym] \"path\"") |
- sc, ev := newScanner(args) |
- |
- var toks [4]token.Token |
- var lits [4]string |
- for i := range toks { |
- _, toks[i], lits[i] = sc.Scan() |
- } |
- if sc.ErrorCount != 0 { |
- err = ev.GetError(scanner.NoMultiples) |
- return |
- } |
- |
- i := 0 |
- switch toks[i] { |
- case token.PERIOD, token.IDENT: |
- ident = string(lits[i]) |
- i++ |
- } |
- |
- if toks[i] != token.STRING { |
- return |
- } |
- path, uerr := strconv.Unquote(string(lits[i])) |
- if uerr != nil { |
- err = uerr |
- return |
- } |
- i++ |
- |
- if toks[i] == token.SEMICOLON { |
- i++ |
- } |
- if toks[i] != token.EOF { |
- return |
- } |
- |
- return ident, path, nil |
-} |
- |
-// cmdBt prints a backtrace for the current goroutine. It takes no |
-// arguments. |
-func cmdBt(args []byte) os.Error { |
- err := parseNoArgs(args, "Usage: bt") |
- if err != nil { |
- return err |
- } |
- |
- if curProc == nil || curProc.curGoroutine == nil { |
- return NoCurrentGoroutine{} |
- } |
- |
- f := curProc.curGoroutine.frame |
- if f == nil { |
- fmt.Println("No frames on stack") |
- return nil |
- } |
- |
- for f.Inner() != nil { |
- f = f.Inner() |
- } |
- |
- for i := 0; i < 100; i++ { |
- if f == curProc.curGoroutine.frame { |
- fmt.Printf("=> ") |
- } else { |
- fmt.Printf(" ") |
- } |
- fmt.Printf("%8x %v\n", f.pc, f) |
- f, err = f.Outer() |
- if err != nil { |
- return err |
- } |
- if f == nil { |
- return nil |
- } |
- } |
- |
- fmt.Println("...") |
- return nil |
-} |
- |
-func parseNoArgs(args []byte, usage string) os.Error { |
- sc, ev := newScanner(args) |
- _, tok, _ := sc.Scan() |
- if sc.ErrorCount != 0 { |
- return ev.GetError(scanner.NoMultiples) |
- } |
- if tok != token.EOF { |
- return UsageError(usage) |
- } |
- return nil |
-} |
- |
-/* |
- * Functions |
- */ |
- |
-// defineFuncs populates world with the built-in functions. |
-func defineFuncs() { |
- t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig) |
- world.DefineConst("Out", t, v) |
- t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig) |
- world.DefineConst("ContWait", t, v) |
- t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig) |
- world.DefineConst("BpSet", t, v) |
-} |
- |
-// printCurFrame prints the current stack frame, as it would appear in |
-// a backtrace. |
-func printCurFrame() { |
- if curProc == nil || curProc.curGoroutine == nil { |
- return |
- } |
- f := curProc.curGoroutine.frame |
- if f == nil { |
- return |
- } |
- fmt.Printf("=> %8x %v\n", f.pc, f) |
-} |
- |
-// fnOut moves the current frame to the caller of the current frame. |
-func fnOutSig() {} |
-func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) { |
- if curProc == nil { |
- t.Abort(NoCurrentGoroutine{}) |
- } |
- err := curProc.Out() |
- if err != nil { |
- t.Abort(err) |
- } |
- // TODO(austin) Only in the command form |
- printCurFrame() |
-} |
- |
-// fnContWait continues the current process and waits for a stopping event. |
-func fnContWaitSig() {} |
-func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) { |
- if curProc == nil { |
- t.Abort(NoCurrentGoroutine{}) |
- } |
- err := curProc.ContWait() |
- if err != nil { |
- t.Abort(err) |
- } |
- // TODO(austin) Only in the command form |
- ev := curProc.Event() |
- if ev != nil { |
- fmt.Printf("%v\n", ev) |
- } |
- printCurFrame() |
-} |
- |
-// fnBpSet sets a breakpoint at the entry to the named function. |
-func fnBpSetSig(string) {} |
-func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { |
- // TODO(austin) This probably shouldn't take a symbol name. |
- // Perhaps it should take an interface that provides PC's. |
- // Functions and instructions can implement that interface and |
- // we can have something to translate file:line pairs. |
- if curProc == nil { |
- t.Abort(NoCurrentGoroutine{}) |
- } |
- name := args[0].(eval.StringValue).Get(t) |
- fn := curProc.syms.LookupFunc(name) |
- if fn == nil { |
- t.Abort(UsageError("no such function " + name)) |
- } |
- curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop) |
-} |