OSDN Git Service

4f67032d0c3085a029149d06037c4e33543f1f36
[pf3gnuchains/gcc-fork.git] / libgo / go / exp / ogle / cmd.go
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.
4
5 // Ogle is the beginning of a debugger for Go.
6 package ogle
7
8 import (
9         "bufio"
10         "debug/elf"
11         "debug/proc"
12         "exp/eval"
13         "fmt"
14         "go/scanner"
15         "go/token"
16         "os"
17         "strconv"
18         "strings"
19 )
20
21 var fset = token.NewFileSet()
22 var world *eval.World
23 var curProc *Process
24
25 func Main() {
26         world = eval.NewWorld()
27         defineFuncs()
28         r := bufio.NewReader(os.Stdin)
29         for {
30                 print("; ")
31                 line, err := r.ReadSlice('\n')
32                 if err != nil {
33                         break
34                 }
35
36                 // Try line as a command
37                 cmd, rest := getCmd(line)
38                 if cmd != nil {
39                         err := cmd.handler(rest)
40                         if err != nil {
41                                 scanner.PrintError(os.Stderr, err)
42                         }
43                         continue
44                 }
45
46                 // Try line as code
47                 code, err := world.Compile(fset, string(line))
48                 if err != nil {
49                         scanner.PrintError(os.Stderr, err)
50                         continue
51                 }
52                 v, err := code.Run()
53                 if err != nil {
54                         fmt.Fprintf(os.Stderr, err.String())
55                         continue
56                 }
57                 if v != nil {
58                         println(v.String())
59                 }
60         }
61 }
62
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)
69         return sc, ev
70 }
71
72 /*
73  * Commands
74  */
75
76 // A UsageError occurs when a command is called with illegal arguments.
77 type UsageError string
78
79 func (e UsageError) String() string { return string(e) }
80
81 // A cmd represents a single command with a handler.
82 type cmd struct {
83         cmd     string
84         handler func([]byte) os.Error
85 }
86
87 var cmds = []cmd{
88         {"load", cmdLoad},
89         {"bt", cmdBt},
90 }
91
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 {
99                 return nil, nil
100         }
101
102         slit := string(lit)
103         for i := range cmds {
104                 if cmds[i].cmd == slit {
105                         return &cmds[i], line[fset.Position(pos).Offset+len(lit):]
106                 }
107         }
108         return nil, nil
109 }
110
111 // cmdLoad starts or attaches to a process.  Its form is similar to
112 // import:
113 //
114 //  load [sym] "path" [;]
115 //
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.
121 //
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.
125 //
126 // load always sets the current process to the loaded process.
127 func cmdLoad(args []byte) os.Error {
128         ident, path, err := parseLoad(args)
129         if err != nil {
130                 return err
131         }
132         if curProc != nil {
133                 return UsageError("multiple processes not implemented")
134         }
135         if ident != "." {
136                 return UsageError("process identifiers not implemented")
137         }
138
139         // Parse argument and start or attach to process
140         var fname string
141         var tproc proc.Process
142         if len(path) >= 4 && path[0:4] == "pid:" {
143                 pid, err := strconv.Atoi(path[4:])
144                 if err != nil {
145                         return err
146                 }
147                 fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
148                 if err != nil {
149                         return err
150                 }
151                 tproc, err = proc.Attach(pid)
152                 if err != nil {
153                         return err
154                 }
155                 println("Attached to", pid)
156         } else {
157                 parts := strings.Split(path, " ", -1)
158                 if len(parts) == 0 {
159                         fname = ""
160                 } else {
161                         fname = parts[0]
162                 }
163                 tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr})
164                 if err != nil {
165                         return err
166                 }
167                 println("Started", path)
168                 // TODO(austin) If we fail after this point, kill tproc
169                 // before detaching.
170         }
171
172         // Get symbols
173         f, err := os.Open(fname, os.O_RDONLY, 0)
174         if err != nil {
175                 tproc.Detach()
176                 return err
177         }
178         defer f.Close()
179         elf, err := elf.NewFile(f)
180         if err != nil {
181                 tproc.Detach()
182                 return err
183         }
184         curProc, err = NewProcessElf(tproc, elf)
185         if err != nil {
186                 tproc.Detach()
187                 return err
188         }
189
190         // Prepare new process
191         curProc.OnGoroutineCreate().AddHandler(EventPrint)
192         curProc.OnGoroutineExit().AddHandler(EventPrint)
193
194         err = curProc.populateWorld(world)
195         if err != nil {
196                 tproc.Detach()
197                 return err
198         }
199
200         return nil
201 }
202
203 func parseLoad(args []byte) (ident string, path string, err os.Error) {
204         err = UsageError("Usage: load [sym] \"path\"")
205         sc, ev := newScanner(args)
206
207         var toks [4]token.Token
208         var lits [4][]byte
209         for i := range toks {
210                 _, toks[i], lits[i] = sc.Scan()
211         }
212         if sc.ErrorCount != 0 {
213                 err = ev.GetError(scanner.NoMultiples)
214                 return
215         }
216
217         i := 0
218         switch toks[i] {
219         case token.PERIOD, token.IDENT:
220                 ident = string(lits[i])
221                 i++
222         }
223
224         if toks[i] != token.STRING {
225                 return
226         }
227         path, uerr := strconv.Unquote(string(lits[i]))
228         if uerr != nil {
229                 err = uerr
230                 return
231         }
232         i++
233
234         if toks[i] == token.SEMICOLON {
235                 i++
236         }
237         if toks[i] != token.EOF {
238                 return
239         }
240
241         return ident, path, nil
242 }
243
244 // cmdBt prints a backtrace for the current goroutine.  It takes no
245 // arguments.
246 func cmdBt(args []byte) os.Error {
247         err := parseNoArgs(args, "Usage: bt")
248         if err != nil {
249                 return err
250         }
251
252         if curProc == nil || curProc.curGoroutine == nil {
253                 return NoCurrentGoroutine{}
254         }
255
256         f := curProc.curGoroutine.frame
257         if f == nil {
258                 fmt.Println("No frames on stack")
259                 return nil
260         }
261
262         for f.Inner() != nil {
263                 f = f.Inner()
264         }
265
266         for i := 0; i < 100; i++ {
267                 if f == curProc.curGoroutine.frame {
268                         fmt.Printf("=> ")
269                 } else {
270                         fmt.Printf("   ")
271                 }
272                 fmt.Printf("%8x %v\n", f.pc, f)
273                 f, err = f.Outer()
274                 if err != nil {
275                         return err
276                 }
277                 if f == nil {
278                         return nil
279                 }
280         }
281
282         fmt.Println("...")
283         return nil
284 }
285
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)
291         }
292         if tok != token.EOF {
293                 return UsageError(usage)
294         }
295         return nil
296 }
297
298 /*
299  * Functions
300  */
301
302 // defineFuncs populates world with the built-in functions.
303 func defineFuncs() {
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)
310 }
311
312 // printCurFrame prints the current stack frame, as it would appear in
313 // a backtrace.
314 func printCurFrame() {
315         if curProc == nil || curProc.curGoroutine == nil {
316                 return
317         }
318         f := curProc.curGoroutine.frame
319         if f == nil {
320                 return
321         }
322         fmt.Printf("=> %8x %v\n", f.pc, f)
323 }
324
325 // fnOut moves the current frame to the caller of the current frame.
326 func fnOutSig() {}
327 func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) {
328         if curProc == nil {
329                 t.Abort(NoCurrentGoroutine{})
330         }
331         err := curProc.Out()
332         if err != nil {
333                 t.Abort(err)
334         }
335         // TODO(austin) Only in the command form
336         printCurFrame()
337 }
338
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) {
342         if curProc == nil {
343                 t.Abort(NoCurrentGoroutine{})
344         }
345         err := curProc.ContWait()
346         if err != nil {
347                 t.Abort(err)
348         }
349         // TODO(austin) Only in the command form
350         ev := curProc.Event()
351         if ev != nil {
352                 fmt.Printf("%v\n", ev)
353         }
354         printCurFrame()
355 }
356
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.
364         if curProc == nil {
365                 t.Abort(NoCurrentGoroutine{})
366         }
367         name := args[0].(eval.StringValue).Get(t)
368         fn := curProc.syms.LookupFunc(name)
369         if fn == nil {
370                 t.Abort(UsageError("no such function " + name))
371         }
372         curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop)
373 }