1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Ogle is the beginning of a debugger for Go.
21 var fset = token.NewFileSet()
26 world = eval.NewWorld()
28 r := bufio.NewReader(os.Stdin)
31 line, err := r.ReadSlice('\n')
36 // Try line as a command
37 cmd, rest := getCmd(line)
39 err := cmd.handler(rest)
41 scanner.PrintError(os.Stderr, err)
47 code, err := world.Compile(fset, string(line))
49 scanner.PrintError(os.Stderr, err)
54 fmt.Fprintf(os.Stderr, err.String())
63 // newScanner creates a new scanner that scans that given input bytes.
64 func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) {
65 sc := new(scanner.Scanner)
66 ev := new(scanner.ErrorVector)
67 file := fset.AddFile("input", fset.Base(), len(input))
68 sc.Init(file, input, ev, 0)
76 // A UsageError occurs when a command is called with illegal arguments.
77 type UsageError string
79 func (e UsageError) String() string { return string(e) }
81 // A cmd represents a single command with a handler.
84 handler func([]byte) os.Error
92 // getCmd attempts to parse an input line as a registered command. If
93 // successful, it returns the command and the bytes remaining after
94 // the command, which should be passed to the command.
95 func getCmd(line []byte) (*cmd, []byte) {
96 sc, _ := newScanner(line)
97 pos, tok, lit := sc.Scan()
98 if sc.ErrorCount != 0 || tok != token.IDENT {
103 for i := range cmds {
104 if cmds[i].cmd == slit {
105 return &cmds[i], line[fset.Position(pos).Offset+len(lit):]
111 // cmdLoad starts or attaches to a process. Its form is similar to
114 // load [sym] "path" [;]
116 // sym specifies the name to give to the process. If not given, the
117 // name is derived from the path of the process. If ".", then the
118 // packages from the remote process are defined into the current
119 // namespace. If given, this symbol is defined as a package
120 // containing the process' packages.
122 // path gives the path of the process to start or attach to. If it is
123 // "pid:<num>", then attach to the given PID. Otherwise, treat it as
124 // a file path and space-separated arguments and start a new process.
126 // load always sets the current process to the loaded process.
127 func cmdLoad(args []byte) os.Error {
128 ident, path, err := parseLoad(args)
133 return UsageError("multiple processes not implemented")
136 return UsageError("process identifiers not implemented")
139 // Parse argument and start or attach to process
141 var tproc proc.Process
142 if len(path) >= 4 && path[0:4] == "pid:" {
143 pid, err := strconv.Atoi(path[4:])
147 fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
151 tproc, err = proc.Attach(pid)
155 println("Attached to", pid)
157 parts := strings.Split(path, " ", -1)
163 tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}})
167 println("Started", path)
168 // TODO(austin) If we fail after this point, kill tproc
173 f, err := os.Open(fname)
179 elf, err := elf.NewFile(f)
184 curProc, err = NewProcessElf(tproc, elf)
190 // Prepare new process
191 curProc.OnGoroutineCreate().AddHandler(EventPrint)
192 curProc.OnGoroutineExit().AddHandler(EventPrint)
194 err = curProc.populateWorld(world)
203 func parseLoad(args []byte) (ident string, path string, err os.Error) {
204 err = UsageError("Usage: load [sym] \"path\"")
205 sc, ev := newScanner(args)
207 var toks [4]token.Token
209 for i := range toks {
210 _, toks[i], lits[i] = sc.Scan()
212 if sc.ErrorCount != 0 {
213 err = ev.GetError(scanner.NoMultiples)
219 case token.PERIOD, token.IDENT:
220 ident = string(lits[i])
224 if toks[i] != token.STRING {
227 path, uerr := strconv.Unquote(string(lits[i]))
234 if toks[i] == token.SEMICOLON {
237 if toks[i] != token.EOF {
241 return ident, path, nil
244 // cmdBt prints a backtrace for the current goroutine. It takes no
246 func cmdBt(args []byte) os.Error {
247 err := parseNoArgs(args, "Usage: bt")
252 if curProc == nil || curProc.curGoroutine == nil {
253 return NoCurrentGoroutine{}
256 f := curProc.curGoroutine.frame
258 fmt.Println("No frames on stack")
262 for f.Inner() != nil {
266 for i := 0; i < 100; i++ {
267 if f == curProc.curGoroutine.frame {
272 fmt.Printf("%8x %v\n", f.pc, f)
286 func parseNoArgs(args []byte, usage string) os.Error {
287 sc, ev := newScanner(args)
288 _, tok, _ := sc.Scan()
289 if sc.ErrorCount != 0 {
290 return ev.GetError(scanner.NoMultiples)
292 if tok != token.EOF {
293 return UsageError(usage)
302 // defineFuncs populates world with the built-in functions.
304 t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig)
305 world.DefineConst("Out", t, v)
306 t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig)
307 world.DefineConst("ContWait", t, v)
308 t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig)
309 world.DefineConst("BpSet", t, v)
312 // printCurFrame prints the current stack frame, as it would appear in
314 func printCurFrame() {
315 if curProc == nil || curProc.curGoroutine == nil {
318 f := curProc.curGoroutine.frame
322 fmt.Printf("=> %8x %v\n", f.pc, f)
325 // fnOut moves the current frame to the caller of the current frame.
327 func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) {
329 t.Abort(NoCurrentGoroutine{})
335 // TODO(austin) Only in the command form
339 // fnContWait continues the current process and waits for a stopping event.
340 func fnContWaitSig() {}
341 func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) {
343 t.Abort(NoCurrentGoroutine{})
345 err := curProc.ContWait()
349 // TODO(austin) Only in the command form
350 ev := curProc.Event()
352 fmt.Printf("%v\n", ev)
357 // fnBpSet sets a breakpoint at the entry to the named function.
358 func fnBpSetSig(string) {}
359 func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
360 // TODO(austin) This probably shouldn't take a symbol name.
361 // Perhaps it should take an interface that provides PC's.
362 // Functions and instructions can implement that interface and
363 // we can have something to translate file:line pairs.
365 t.Abort(NoCurrentGoroutine{})
367 name := args[0].(eval.StringValue).Get(t)
368 fn := curProc.syms.LookupFunc(name)
370 t.Abort(UsageError("no such function " + name))
372 curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop)