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.
25 world
= eval
.NewWorld()
27 r
:= bufio
.NewReader(os
.Stdin
)
30 line
, err
:= r
.ReadSlice('\n')
35 // Try line as a command
36 cmd
, rest
:= getCmd(line
)
38 err
:= cmd
.handler(rest
)
40 scanner
.PrintError(os
.Stderr
, err
)
46 code
, err
:= world
.Compile(string(line
))
48 scanner
.PrintError(os
.Stderr
, err
)
53 fmt
.Fprintf(os
.Stderr
, err
.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)
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.
83 handler
func([]byte) os
.Error
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
{
102 for i
:= range cmds
{
103 if cmds
[i
].cmd
== slit
{
104 return &cmds
[i
], line
[pos
.Offset
+len(lit
):]
110 // cmdLoad starts or attaches to a process. Its form is similar to
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
)
132 return UsageError("multiple processes not implemented")
135 return UsageError("process identifiers not implemented")
138 // Parse argument and start or attach to process
140 var tproc proc
.Process
141 if len(path
) >= 4 && path
[0:4] == "pid:" {
142 pid
, err
:= strconv
.Atoi(path
[4:])
146 fname
, err
= os
.Readlink(fmt
.Sprintf("/proc/%d/exe", pid
))
150 tproc
, err
= proc
.Attach(pid
)
154 println("Attached to", pid
)
156 parts
:= strings
.Split(path
, " ", -1)
162 tproc
, err
= proc
.ForkExec(fname
, parts
, os
.Environ(), "", []*os
.File
{os
.Stdin
, os
.Stdout
, os
.Stderr
})
166 println("Started", path
)
167 // TODO(austin) If we fail after this point, kill tproc
172 f
, err
:= os
.Open(fname
, os
.O_RDONLY
, 0)
178 elf
, err
:= elf
.NewFile(f
)
183 curProc
, err
= NewProcessElf(tproc
, elf
)
189 // Prepare new process
190 curProc
.OnGoroutineCreate().AddHandler(EventPrint
)
191 curProc
.OnGoroutineExit().AddHandler(EventPrint
)
193 err
= curProc
.populateWorld(world
)
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
208 for i
:= range toks
{
209 _
, toks
[i
], lits
[i
] = sc
.Scan()
211 if sc
.ErrorCount
!= 0 {
212 err
= ev
.GetError(scanner
.NoMultiples
)
218 case token
.PERIOD
, token
.IDENT
:
219 ident
= string(lits
[i
])
223 if toks
[i
] != token
.STRING
{
226 path
, uerr
:= strconv
.Unquote(string(lits
[i
]))
233 if toks
[i
] == token
.SEMICOLON
{
236 if toks
[i
] != token
.EOF
{
240 return ident
, path
, nil
243 // cmdBt prints a backtrace for the current goroutine. It takes no
245 func cmdBt(args
[]byte) os
.Error
{
246 err
:= parseNoArgs(args
, "Usage: bt")
251 if curProc
== nil || curProc
.curGoroutine
== nil {
252 return NoCurrentGoroutine
{}
255 f
:= curProc
.curGoroutine
.frame
257 fmt
.Println("No frames on stack")
261 for f
.Inner() != nil {
265 for i
:= 0; i
< 100; i
++ {
266 if f
== curProc
.curGoroutine
.frame
{
271 fmt
.Printf("%8x %v\n", f
.pc
, f
)
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
)
301 // defineFuncs populates world with the built-in functions.
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
313 func printCurFrame() {
314 if curProc
== nil || curProc
.curGoroutine
== nil {
317 f
:= curProc
.curGoroutine
.frame
321 fmt
.Printf("=> %8x %v\n", f
.pc
, f
)
324 // fnOut moves the current frame to the caller of the current frame.
326 func fnOut(t
*eval
.Thread
, args
[]eval
.Value
, res
[]eval
.Value
) {
328 t
.Abort(NoCurrentGoroutine
{})
334 // TODO(austin) Only in the command form
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
) {
342 t
.Abort(NoCurrentGoroutine
{})
344 err
:= curProc
.ContWait()
348 // TODO(austin) Only in the command form
349 ev
:= curProc
.Event()
351 fmt
.Printf("%v\n", ev
)
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.
364 t
.Abort(NoCurrentGoroutine
{})
366 name
:= args
[0].(eval
.StringValue
).Get(t
)
367 fn
:= curProc
.syms
.LookupFunc(name
)
369 t
.Abort(UsageError("no such function " + name
))
371 curProc
.OnBreakpoint(proc
.Word(fn
.Entry
)).AddHandler(EventStop
)