7a9389330e
gcc/: * gcc.c (default_compilers): Add entry for ".go". * common.opt: Add -static-libgo as a driver option. * doc/install.texi (Configuration): Mention libgo as an option for --enable-shared. Mention go as an option for --enable-languages. * doc/invoke.texi (Overall Options): Mention .go as a file name suffix. Mention go as a -x option. * doc/frontends.texi (G++ and GCC): Mention Go as a supported language. * doc/sourcebuild.texi (Top Level): Mention libgo. * doc/standards.texi (Standards): Add section on Go language. Move references for other languages into their own section. * doc/contrib.texi (Contributors): Mention that I contributed the Go frontend. gcc/testsuite/: * lib/go.exp: New file. * lib/go-dg.exp: New file. * lib/go-torture.exp: New file. * lib/target-supports.exp (check_compile): Match // Go. From-SVN: r167407
281 lines
6.8 KiB
Go
281 lines
6.8 KiB
Go
// 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
|
|
|
|
import (
|
|
"debug/proc"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
/*
|
|
* Hooks and events
|
|
*/
|
|
|
|
// An EventHandler is a function that takes an event and returns a
|
|
// response to that event and possibly an error. If an event handler
|
|
// returns an error, the process stops and no other handlers for that
|
|
// event are executed.
|
|
type EventHandler func(e Event) (EventAction, os.Error)
|
|
|
|
// An EventAction is an event handler's response to an event. If all
|
|
// of an event's handlers execute without returning errors, their
|
|
// results are combined as follows: If any handler returned
|
|
// EAContinue, then the process resumes (without returning from
|
|
// WaitStop); otherwise, if any handler returned EAStop, the process
|
|
// remains stopped; otherwise, if all handlers returned EADefault, the
|
|
// process resumes. A handler may return EARemoveSelf bit-wise or'd
|
|
// with any other action to indicate that the handler should be
|
|
// removed from the hook.
|
|
type EventAction int
|
|
|
|
const (
|
|
EARemoveSelf EventAction = 0x100
|
|
EADefault EventAction = iota
|
|
EAStop
|
|
EAContinue
|
|
)
|
|
|
|
// A EventHook allows event handlers to be added and removed.
|
|
type EventHook interface {
|
|
AddHandler(EventHandler)
|
|
RemoveHandler(EventHandler)
|
|
NumHandler() int
|
|
handle(e Event) (EventAction, os.Error)
|
|
String() string
|
|
}
|
|
|
|
// EventHook is almost, but not quite, suitable for user-defined
|
|
// events. If we want user-defined events, make EventHook a struct,
|
|
// special-case adding and removing handlers in breakpoint hooks, and
|
|
// provide a public interface for posting events to hooks.
|
|
|
|
type Event interface {
|
|
Process() *Process
|
|
Goroutine() *Goroutine
|
|
String() string
|
|
}
|
|
|
|
type commonHook struct {
|
|
// Head of handler chain
|
|
head *handler
|
|
// Number of non-internal handlers
|
|
len int
|
|
}
|
|
|
|
type handler struct {
|
|
eh EventHandler
|
|
// True if this handler must be run before user-defined
|
|
// handlers in order to ensure correctness.
|
|
internal bool
|
|
// True if this handler has been removed from the chain.
|
|
removed bool
|
|
next *handler
|
|
}
|
|
|
|
func (h *commonHook) AddHandler(eh EventHandler) {
|
|
h.addHandler(eh, false)
|
|
}
|
|
|
|
func (h *commonHook) addHandler(eh EventHandler, internal bool) {
|
|
// Ensure uniqueness of handlers
|
|
h.RemoveHandler(eh)
|
|
|
|
if !internal {
|
|
h.len++
|
|
}
|
|
// Add internal handlers to the beginning
|
|
if internal || h.head == nil {
|
|
h.head = &handler{eh, internal, false, h.head}
|
|
return
|
|
}
|
|
// Add handler after internal handlers
|
|
// TODO(austin) This should probably go on the end instead
|
|
prev := h.head
|
|
for prev.next != nil && prev.internal {
|
|
prev = prev.next
|
|
}
|
|
prev.next = &handler{eh, internal, false, prev.next}
|
|
}
|
|
|
|
func (h *commonHook) RemoveHandler(eh EventHandler) {
|
|
plink := &h.head
|
|
for l := *plink; l != nil; plink, l = &l.next, l.next {
|
|
if l.eh == eh {
|
|
if !l.internal {
|
|
h.len--
|
|
}
|
|
l.removed = true
|
|
*plink = l.next
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *commonHook) NumHandler() int { return h.len }
|
|
|
|
func (h *commonHook) handle(e Event) (EventAction, os.Error) {
|
|
action := EADefault
|
|
plink := &h.head
|
|
for l := *plink; l != nil; plink, l = &l.next, l.next {
|
|
if l.removed {
|
|
continue
|
|
}
|
|
a, err := l.eh(e)
|
|
if a&EARemoveSelf == EARemoveSelf {
|
|
if !l.internal {
|
|
h.len--
|
|
}
|
|
l.removed = true
|
|
*plink = l.next
|
|
a &^= EARemoveSelf
|
|
}
|
|
if err != nil {
|
|
return EAStop, err
|
|
}
|
|
if a > action {
|
|
action = a
|
|
}
|
|
}
|
|
return action, nil
|
|
}
|
|
|
|
type commonEvent struct {
|
|
// The process of this event
|
|
p *Process
|
|
// The goroutine of this event.
|
|
t *Goroutine
|
|
}
|
|
|
|
func (e *commonEvent) Process() *Process { return e.p }
|
|
|
|
func (e *commonEvent) Goroutine() *Goroutine { return e.t }
|
|
|
|
/*
|
|
* Standard event handlers
|
|
*/
|
|
|
|
// EventPrint is a standard event handler that prints events as they
|
|
// occur. It will not cause the process to stop.
|
|
func EventPrint(ev Event) (EventAction, os.Error) {
|
|
// TODO(austin) Include process name here?
|
|
fmt.Fprintf(os.Stderr, "*** %v\n", ev.String())
|
|
return EADefault, nil
|
|
}
|
|
|
|
// EventStop is a standard event handler that causes the process to stop.
|
|
func EventStop(ev Event) (EventAction, os.Error) {
|
|
return EAStop, nil
|
|
}
|
|
|
|
/*
|
|
* Breakpoints
|
|
*/
|
|
|
|
type breakpointHook struct {
|
|
commonHook
|
|
p *Process
|
|
pc proc.Word
|
|
}
|
|
|
|
// A Breakpoint event occurs when a process reaches a particular
|
|
// program counter. When this event is handled, the current goroutine
|
|
// will be the goroutine that reached the program counter.
|
|
type Breakpoint struct {
|
|
commonEvent
|
|
osThread proc.Thread
|
|
pc proc.Word
|
|
}
|
|
|
|
func (h *breakpointHook) AddHandler(eh EventHandler) {
|
|
h.addHandler(eh, false)
|
|
}
|
|
|
|
func (h *breakpointHook) addHandler(eh EventHandler, internal bool) {
|
|
// We register breakpoint events lazily to avoid holding
|
|
// references to breakpoints without handlers. Be sure to use
|
|
// the "canonical" breakpoint if there is one.
|
|
if cur, ok := h.p.breakpointHooks[h.pc]; ok {
|
|
h = cur
|
|
}
|
|
oldhead := h.head
|
|
h.commonHook.addHandler(eh, internal)
|
|
if oldhead == nil && h.head != nil {
|
|
h.p.proc.AddBreakpoint(h.pc)
|
|
h.p.breakpointHooks[h.pc] = h
|
|
}
|
|
}
|
|
|
|
func (h *breakpointHook) RemoveHandler(eh EventHandler) {
|
|
oldhead := h.head
|
|
h.commonHook.RemoveHandler(eh)
|
|
if oldhead != nil && h.head == nil {
|
|
h.p.proc.RemoveBreakpoint(h.pc)
|
|
h.p.breakpointHooks[h.pc] = nil, false
|
|
}
|
|
}
|
|
|
|
func (h *breakpointHook) String() string {
|
|
// TODO(austin) Include process name?
|
|
// TODO(austin) Use line:pc or at least sym+%#x
|
|
return fmt.Sprintf("breakpoint at %#x", h.pc)
|
|
}
|
|
|
|
func (b *Breakpoint) PC() proc.Word { return b.pc }
|
|
|
|
func (b *Breakpoint) String() string {
|
|
// TODO(austin) Include process name and goroutine
|
|
// TODO(austin) Use line:pc or at least sym+%#x
|
|
return fmt.Sprintf("breakpoint at %#x", b.pc)
|
|
}
|
|
|
|
/*
|
|
* Goroutine create/exit
|
|
*/
|
|
|
|
type goroutineCreateHook struct {
|
|
commonHook
|
|
}
|
|
|
|
func (h *goroutineCreateHook) String() string { return "goroutine create" }
|
|
|
|
// A GoroutineCreate event occurs when a process creates a new
|
|
// goroutine. When this event is handled, the current goroutine will
|
|
// be the newly created goroutine.
|
|
type GoroutineCreate struct {
|
|
commonEvent
|
|
parent *Goroutine
|
|
}
|
|
|
|
// Parent returns the goroutine that created this goroutine. May be
|
|
// nil if this event is the creation of the first goroutine.
|
|
func (e *GoroutineCreate) Parent() *Goroutine { return e.parent }
|
|
|
|
func (e *GoroutineCreate) String() string {
|
|
// TODO(austin) Include process name
|
|
if e.parent == nil {
|
|
return fmt.Sprintf("%v created", e.t)
|
|
}
|
|
return fmt.Sprintf("%v created by %v", e.t, e.parent)
|
|
}
|
|
|
|
type goroutineExitHook struct {
|
|
commonHook
|
|
}
|
|
|
|
func (h *goroutineExitHook) String() string { return "goroutine exit" }
|
|
|
|
// A GoroutineExit event occurs when a Go goroutine exits.
|
|
type GoroutineExit struct {
|
|
commonEvent
|
|
}
|
|
|
|
func (e *GoroutineExit) String() string {
|
|
// TODO(austin) Include process name
|
|
//return fmt.Sprintf("%v exited", e.t);
|
|
// For debugging purposes
|
|
return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base)
|
|
}
|