Merge from mainline (167278:168000).
[official-gcc/graphite-test-results.git] / libgo / go / exp / ogle / cmd.go
blobd3672c24e382968adde6d2fb94f9a40e524ec1ec
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.
6 package ogle
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"
21 var world *eval.World
22 var curProc *Process
24 func Main() {
25 world = eval.NewWorld()
26 defineFuncs()
27 r := bufio.NewReader(os.Stdin)
28 for {
29 print("; ")
30 line, err := r.ReadSlice('\n')
31 if err != nil {
32 break
35 // Try line as a command
36 cmd, rest := getCmd(line)
37 if cmd != nil {
38 err := cmd.handler(rest)
39 if err != nil {
40 scanner.PrintError(os.Stderr, err)
42 continue
45 // Try line as code
46 code, err := world.Compile(string(line))
47 if err != nil {
48 scanner.PrintError(os.Stderr, err)
49 continue
51 v, err := code.Run()
52 if err != nil {
53 fmt.Fprintf(os.Stderr, err.String())
54 continue
56 if v != nil {
57 println(v.String())
62 // newScanner creates a new scanner that scans that given input bytes.
63 func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) {
64 sc := new(scanner.Scanner)
65 ev := new(scanner.ErrorVector)
66 sc.Init("input", input, ev, 0)
68 return sc, ev
72 * Commands
75 // A UsageError occurs when a command is called with illegal arguments.
76 type UsageError string
78 func (e UsageError) String() string { return string(e) }
80 // A cmd represents a single command with a handler.
81 type cmd struct {
82 cmd string
83 handler func([]byte) os.Error
86 var cmds = []cmd{
87 {"load", cmdLoad},
88 {"bt", cmdBt},
91 // getCmd attempts to parse an input line as a registered command. If
92 // successful, it returns the command and the bytes remaining after
93 // the command, which should be passed to the command.
94 func getCmd(line []byte) (*cmd, []byte) {
95 sc, _ := newScanner(line)
96 pos, tok, lit := sc.Scan()
97 if sc.ErrorCount != 0 || tok != token.IDENT {
98 return nil, nil
101 slit := string(lit)
102 for i := range cmds {
103 if cmds[i].cmd == slit {
104 return &cmds[i], line[pos.Offset+len(lit):]
107 return nil, nil
110 // cmdLoad starts or attaches to a process. Its form is similar to
111 // import:
113 // load [sym] "path" [;]
115 // sym specifies the name to give to the process. If not given, the
116 // name is derived from the path of the process. If ".", then the
117 // packages from the remote process are defined into the current
118 // namespace. If given, this symbol is defined as a package
119 // containing the process' packages.
121 // path gives the path of the process to start or attach to. If it is
122 // "pid:<num>", then attach to the given PID. Otherwise, treat it as
123 // a file path and space-separated arguments and start a new process.
125 // load always sets the current process to the loaded process.
126 func cmdLoad(args []byte) os.Error {
127 ident, path, err := parseLoad(args)
128 if err != nil {
129 return err
131 if curProc != nil {
132 return UsageError("multiple processes not implemented")
134 if ident != "." {
135 return UsageError("process identifiers not implemented")
138 // Parse argument and start or attach to process
139 var fname string
140 var tproc proc.Process
141 if len(path) >= 4 && path[0:4] == "pid:" {
142 pid, err := strconv.Atoi(path[4:])
143 if err != nil {
144 return err
146 fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
147 if err != nil {
148 return err
150 tproc, err = proc.Attach(pid)
151 if err != nil {
152 return err
154 println("Attached to", pid)
155 } else {
156 parts := strings.Split(path, " ", -1)
157 if len(parts) == 0 {
158 fname = ""
159 } else {
160 fname = parts[0]
162 tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr})
163 if err != nil {
164 return err
166 println("Started", path)
167 // TODO(austin) If we fail after this point, kill tproc
168 // before detaching.
171 // Get symbols
172 f, err := os.Open(fname, os.O_RDONLY, 0)
173 if err != nil {
174 tproc.Detach()
175 return err
177 defer f.Close()
178 elf, err := elf.NewFile(f)
179 if err != nil {
180 tproc.Detach()
181 return err
183 curProc, err = NewProcessElf(tproc, elf)
184 if err != nil {
185 tproc.Detach()
186 return err
189 // Prepare new process
190 curProc.OnGoroutineCreate().AddHandler(EventPrint)
191 curProc.OnGoroutineExit().AddHandler(EventPrint)
193 err = curProc.populateWorld(world)
194 if err != nil {
195 tproc.Detach()
196 return err
199 return nil
202 func parseLoad(args []byte) (ident string, path string, err os.Error) {
203 err = UsageError("Usage: load [sym] \"path\"")
204 sc, ev := newScanner(args)
206 var toks [4]token.Token
207 var lits [4][]byte
208 for i := range toks {
209 _, toks[i], lits[i] = sc.Scan()
211 if sc.ErrorCount != 0 {
212 err = ev.GetError(scanner.NoMultiples)
213 return
216 i := 0
217 switch toks[i] {
218 case token.PERIOD, token.IDENT:
219 ident = string(lits[i])
223 if toks[i] != token.STRING {
224 return
226 path, uerr := strconv.Unquote(string(lits[i]))
227 if uerr != nil {
228 err = uerr
229 return
233 if toks[i] == token.SEMICOLON {
236 if toks[i] != token.EOF {
237 return
240 return ident, path, nil
243 // cmdBt prints a backtrace for the current goroutine. It takes no
244 // arguments.
245 func cmdBt(args []byte) os.Error {
246 err := parseNoArgs(args, "Usage: bt")
247 if err != nil {
248 return err
251 if curProc == nil || curProc.curGoroutine == nil {
252 return NoCurrentGoroutine{}
255 f := curProc.curGoroutine.frame
256 if f == nil {
257 fmt.Println("No frames on stack")
258 return nil
261 for f.Inner() != nil {
262 f = f.Inner()
265 for i := 0; i < 100; i++ {
266 if f == curProc.curGoroutine.frame {
267 fmt.Printf("=> ")
268 } else {
269 fmt.Printf(" ")
271 fmt.Printf("%8x %v\n", f.pc, f)
272 f, err = f.Outer()
273 if err != nil {
274 return err
276 if f == nil {
277 return nil
281 fmt.Println("...")
282 return nil
285 func parseNoArgs(args []byte, usage string) os.Error {
286 sc, ev := newScanner(args)
287 _, tok, _ := sc.Scan()
288 if sc.ErrorCount != 0 {
289 return ev.GetError(scanner.NoMultiples)
291 if tok != token.EOF {
292 return UsageError(usage)
294 return nil
298 * Functions
301 // defineFuncs populates world with the built-in functions.
302 func defineFuncs() {
303 t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig)
304 world.DefineConst("Out", t, v)
305 t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig)
306 world.DefineConst("ContWait", t, v)
307 t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig)
308 world.DefineConst("BpSet", t, v)
311 // printCurFrame prints the current stack frame, as it would appear in
312 // a backtrace.
313 func printCurFrame() {
314 if curProc == nil || curProc.curGoroutine == nil {
315 return
317 f := curProc.curGoroutine.frame
318 if f == nil {
319 return
321 fmt.Printf("=> %8x %v\n", f.pc, f)
324 // fnOut moves the current frame to the caller of the current frame.
325 func fnOutSig() {}
326 func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) {
327 if curProc == nil {
328 t.Abort(NoCurrentGoroutine{})
330 err := curProc.Out()
331 if err != nil {
332 t.Abort(err)
334 // TODO(austin) Only in the command form
335 printCurFrame()
338 // fnContWait continues the current process and waits for a stopping event.
339 func fnContWaitSig() {}
340 func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) {
341 if curProc == nil {
342 t.Abort(NoCurrentGoroutine{})
344 err := curProc.ContWait()
345 if err != nil {
346 t.Abort(err)
348 // TODO(austin) Only in the command form
349 ev := curProc.Event()
350 if ev != nil {
351 fmt.Printf("%v\n", ev)
353 printCurFrame()
356 // fnBpSet sets a breakpoint at the entry to the named function.
357 func fnBpSetSig(string) {}
358 func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
359 // TODO(austin) This probably shouldn't take a symbol name.
360 // Perhaps it should take an interface that provides PC's.
361 // Functions and instructions can implement that interface and
362 // we can have something to translate file:line pairs.
363 if curProc == nil {
364 t.Abort(NoCurrentGoroutine{})
366 name := args[0].(eval.StringValue).Get(t)
367 fn := curProc.syms.LookupFunc(name)
368 if fn == nil {
369 t.Abort(UsageError("no such function " + name))
371 curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop)