]> Cypherpunks repositories - gostls13.git/commitdiff
Rudimentary command shell for Ogle. Hack to prevent linker
authorAustin Clements <aclements@csail.mit.edu>
Fri, 18 Sep 2009 16:11:19 +0000 (09:11 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Fri, 18 Sep 2009 16:11:19 +0000 (09:11 -0700)
from inlining newprocreadylocked.  Fix type bridge's handling
of basic types.  Include interpreter's Thread in bridged
native function calls.

; load . "6.out"
Started 6.out
; BpSet("main·merge")
; ContWait()
breakpoint at 0x400800
=>   400800 main·merge /home/austin/src-go1/usr/austin/ptrace/test/sort.go:19
; bt
=>   400800 main·merge /home/austin/src-go1/usr/austin/ptrace/test/sort.go:19
     400b6a main·mergeSort+0x1be /home/austin/src-go1/usr/austin/ptrace/test/sort.go:34
     448313 goexit /home/austin/src-go1/src/pkg/runtime/proc.c:133
; main.merge.a
{1}

; load . "pid:25753"
Attached to 25753
; bt
=>   479ddf syscall·Syscall+0x24 /home/austin/src-go1/src/pkg/syscall/asm_linux_amd64.s:24
     47c011 syscall·Read+0x5d /home/austin/src-go1/src/pkg/syscall/zsyscall_linux_amd64.go:368
     4119e5 os·*File·Read+0x5f /home/austin/src-go1/src/pkg/os/file.go:122
     427bf3 bufio·*Reader·fill+0x116 /home/austin/src-go1/src/pkg/bufio/bufio.go:105
     428361 bufio·*Reader·ReadSlice+0x195 /home/austin/src-go1/src/pkg/bufio/bufio.go:244
     40204a ogle·Main+0x94 /home/austin/src-go1/usr/austin/ogle/cmd.go:226
     40080f main·main+0xf /home/austin/src-go1/usr/austin/ogle/main.go:6
     41c4b8 mainstart+0xf /home/austin/src-go1/src/pkg/runtime/amd64/asm.s:55
     41531f goexit /home/austin/src-go1/src/pkg/runtime/proc.c:133

R=rsc
APPROVED=rsc
DELTA=433  (420 added, 2 deleted, 11 changed)
OCL=34410
CL=34782

src/pkg/runtime/proc.c
usr/austin/eval/bridge.go
usr/austin/ogle/Makefile [new file with mode: 0644]
usr/austin/ogle/cmd.go [new file with mode: 0644]
usr/austin/ogle/goroutine.go
usr/austin/ogle/main.go [new file with mode: 0644]
usr/austin/ogle/process.go

index 06859b09ca02bf9b3ec886d324072cb1b9d66fe4..590c277dde9b159fa891115f7f3f915b62e99702 100644 (file)
@@ -265,12 +265,18 @@ readylocked(G *g)
                matchmg();
 }
 
+static void
+nop(void)
+{
+}
+
 // Same as readylocked but a different symbol so that
 // debuggers can set a breakpoint here and catch all
 // new goroutines.
 static void
 newprocreadylocked(G *g)
 {
+       nop();  // avoid inlining in 6l
        readylocked(g);
 }
 
index 41674860e280255d460a50e28423113eb4c7daae..da2dd52a9af5b26a33250a037b45986c1918d88d 100644 (file)
@@ -117,8 +117,10 @@ func TypeFromNative(t reflect.Type) Type {
        }
 
        if nt != nil {
-               nt.Complete(et);
-               et = nt;
+               if _, ok := et.(*NamedType); !ok {
+                       nt.Complete(et);
+                       et = nt;
+               }
        }
 
        nativeTypes[et] = t;
@@ -137,7 +139,7 @@ func TypeOfNative(v interface {}) Type {
  */
 
 type nativeFunc struct {
-       fn func([]Value, []Value);
+       fn func(*Thread, []Value, []Value);
        in, out int;
 }
 
@@ -147,14 +149,14 @@ func (f *nativeFunc) NewFrame() *Frame {
 }
 
 func (f *nativeFunc) Call(t *Thread) {
-       f.fn(t.f.Vars[0:f.in], t.f.Vars[f.in:f.in+f.out]);
+       f.fn(t, t.f.Vars[0:f.in], t.f.Vars[f.in:f.in+f.out]);
 }
 
 // FuncFromNative creates an interpreter function from a native
 // function that takes its in and out arguments as slices of
 // interpreter Value's.  While somewhat inconvenient, this avoids
 // value marshalling.
-func FuncFromNative(fn func([]Value, []Value), t *FuncType) FuncValue {
+func FuncFromNative(fn func(*Thread, []Value, []Value), t *FuncType) FuncValue {
        return &funcV{&nativeFunc{fn, len(t.In), len(t.Out)}};
 }
 
@@ -162,7 +164,7 @@ func FuncFromNative(fn func([]Value, []Value), t *FuncType) FuncValue {
 // function type from a function pointer using reflection.  Typically,
 // the type will be given as a nil pointer to a function with the
 // desired signature.
-func FuncFromNativeTyped(fn func([]Value, []Value), t interface{}) (*FuncType, FuncValue) {
+func FuncFromNativeTyped(fn func(*Thread, []Value, []Value), t interface{}) (*FuncType, FuncValue) {
        ft := TypeOfNative(t).(*FuncType);
        return ft, FuncFromNative(fn, ft);
 }
diff --git a/usr/austin/ogle/Makefile b/usr/austin/ogle/Makefile
new file mode 100644 (file)
index 0000000..a169d06
--- /dev/null
@@ -0,0 +1,27 @@
+# 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.
+
+include $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=ogle
+GOFILES=\
+       abort.go\
+       arch.go\
+       cmd.go\
+       event.go\
+       frame.go\
+       goroutine.go\
+       rruntime.go\
+       rtype.go\
+       rvalue.go\
+       process.go\
+       vars.go\
+
+include $(GOROOT)/src/Make.pkg
+
+main.6: main.go
+       $(GC) -I_obj $<
+
+ogle: main.6 package
+       $(LD) -L_obj -o $@ $<
diff --git a/usr/austin/ogle/cmd.go b/usr/austin/ogle/cmd.go
new file mode 100644 (file)
index 0000000..e240942
--- /dev/null
@@ -0,0 +1,379 @@
+// 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 (
+       "bufio";
+       "eval";
+       "fmt";
+       "go/scanner";
+       "go/token";
+       "os";
+       "ptrace";
+       "strconv";
+       "strings";
+       "sym";
+)
+
+var world *eval.World;
+var curProc *Process
+
+func Main() {
+       world = eval.NewWorld();
+       defineFuncs();
+       r := bufio.NewReader(os.Stdin);
+       for {
+               print("; ");
+               line, err := r.ReadSlice('\n');
+               if err != nil {
+                       break;
+               }
+
+               // Try line as a command
+               cmd, rest := getCmd(line);
+               if cmd != nil {
+                       err := cmd.handler(rest);
+                       if err != nil {
+                               scanner.PrintError(os.Stderr, err);
+                       }
+                       continue;
+               }
+
+               // Try line as code
+               code, err := world.Compile(string(line));
+               if err != nil {
+                       scanner.PrintError(os.Stderr, err);
+                       continue;
+               }
+               v, err := code.Run();
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, err.String());
+                       continue;
+               }
+               if v != nil {
+                       println(v.String());
+               }
+       }
+}
+
+// newScanner creates a new scanner that scans that given input bytes.
+func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) {
+       sc := new(scanner.Scanner);
+       ev := new(scanner.ErrorVector);
+       ev.Init();
+       sc.Init("input", input, ev, 0);
+
+       return sc, ev;
+}
+
+/*
+ * Commands
+ */
+
+// A UsageError occurs when a command is called with illegal arguments.
+type UsageError string;
+
+func (e UsageError) String() string {
+       return string(e);
+}
+
+// A cmd represents a single command with a handler.
+type cmd struct {
+       cmd string;
+       handler func([]byte) os.Error;
+}
+
+var cmds = []cmd {
+       cmd{"load", cmdLoad},
+       cmd{"bt", cmdBt},
+}
+
+// getCmd attempts to parse an input line as a registered command.  If
+// successful, it returns the command and the bytes remaining after
+// the command, which should be passed to the command.
+func getCmd(line []byte) (*cmd, []byte) {
+       sc, ev := newScanner(line);
+       pos, tok, lit := sc.Scan();
+       if sc.ErrorCount != 0 || tok != token.IDENT {
+               return nil, nil;
+       }
+
+       slit := string(lit);
+       for i := range cmds {
+               if cmds[i].cmd == slit {
+                       return &cmds[i], line[pos.Offset + len(lit):len(line)];
+               }
+       }
+       return nil, nil;
+}
+
+// cmdLoad starts or attaches to a process.  Its form is similar to
+// import:
+//
+//  load [sym] "path" [;]
+//
+// sym specifies the name to give to the process.  If not given, the
+// name is derived from the path of the process.  If ".", then the
+// packages from the remote process are defined into the current
+// namespace.  If given, this symbol is defined as a package
+// containing the process' packages.
+//
+// path gives the path of the process to start or attach to.  If it is
+// "pid:<num>", then attach to the given PID.  Otherwise, treat it as
+// a file path and space-separated arguments and start a new process.
+//
+// load always sets the current process to the loaded process.
+func cmdLoad(args []byte) os.Error {
+       ident, path, err := parseLoad(args);
+       if err != nil {
+               return err;
+       }
+       if curProc != nil {
+               return UsageError("multiple processes not implemented");
+       }
+       if ident != "." {
+               return UsageError("process identifiers not implemented");
+       }
+
+       // Parse argument and start or attach to process
+       var fname string;
+       var proc ptrace.Process;
+       if len(path) >= 4 && path[0:4] == "pid:" {
+               pid, err := strconv.Atoi(path[4:len(path)]);
+               if err != nil {
+                       return err;
+               }
+               fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid));
+               if err != nil {
+                       return err;
+               }
+               proc, err = ptrace.Attach(pid);
+               if err != nil {
+                       return err;
+               }
+               println("Attached to", pid);
+       } else {
+               parts := strings.Split(path, " ", 0);
+               if len(parts) == 0 {
+                       fname = "";
+               } else {
+                       fname = parts[0];
+               }
+               proc, err = ptrace.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr});
+               if err != nil {
+                       return err;
+               }
+               println("Started", path);
+               // TODO(austin) If we fail after this point, kill proc
+               // before detaching.
+       }
+
+       // Get symbols
+       f, err := os.Open(fname, os.O_RDONLY, 0);
+       if err != nil {
+               proc.Detach();
+               return err;
+       }
+       defer f.Close();
+       elf, err := sym.NewElf(f);
+       if err != nil {
+               proc.Detach();
+               return err;
+       }
+       curProc, err = NewProcessElf(proc, elf);
+       if err != nil {
+               proc.Detach();
+               return err;
+       }
+
+       // Prepare new process
+       curProc.OnGoroutineCreate().AddHandler(EventPrint);
+       curProc.OnGoroutineExit().AddHandler(EventPrint);
+
+       err = curProc.populateWorld(world);
+       if err != nil {
+               proc.Detach();
+               return err;
+       }
+
+       return nil;
+}
+
+func parseLoad(args []byte) (ident string, path string, err os.Error) {
+       err = UsageError("Usage: load [sym] \"path\"");
+       sc, ev := newScanner(args);
+
+       var toks [4]token.Token;
+       var lits [4][]byte;
+       for i := range toks {
+               var pos token.Position;
+               pos, toks[i], lits[i] = sc.Scan();
+       }
+       if sc.ErrorCount != 0 {
+               err = ev.GetError(scanner.NoMultiples);
+               return;
+       }
+
+       i := 0;
+       switch toks[i] {
+       case token.PERIOD, token.IDENT:
+               ident = string(lits[i]);
+               i++;
+       }
+
+       if toks[i] != token.STRING {
+               return;
+       }
+       path, uerr := strconv.Unquote(string(lits[i]));
+       if uerr != nil {
+               err = uerr;
+               return;
+       }
+       i++;
+
+       if toks[i] == token.SEMICOLON {
+               i++;
+       }
+       if toks[i] != token.EOF {
+               return;
+       }
+
+       return ident, path, nil;
+}
+
+// cmdBt prints a backtrace for the current goroutine.  It takes no
+// arguments.
+func cmdBt(args []byte) os.Error {
+       err := parseNoArgs(args, "Usage: bt");
+       if err != nil {
+               return err;
+       }
+
+       if curProc == nil || curProc.curGoroutine == nil {
+               return NoCurrentGoroutine{};
+       }
+
+       f := curProc.curGoroutine.frame;
+       if f == nil {
+               fmt.Println("No frames on stack");
+               return nil;
+       }
+
+       for f.Inner() != nil {
+               f = f.Inner();
+       }
+
+       for i := 0; i < 100; i++ {
+               if f == curProc.curGoroutine.frame {
+                       fmt.Printf("=> ");
+               } else {
+                       fmt.Printf("   ");
+               }
+               fmt.Printf("%8x %v\n", f.pc, f);
+               f, err = f.Outer();
+               if err != nil {
+                       return err;
+               }
+               if f == nil {
+                       return nil;
+               }
+       }
+
+       fmt.Println("...");
+       return nil;
+}
+
+func parseNoArgs(args []byte, usage string) os.Error {
+       sc, ev := newScanner(args);
+       pos, tok, lit := sc.Scan();
+       if sc.ErrorCount != 0 {
+               return ev.GetError(scanner.NoMultiples);
+       }
+       if tok != token.EOF {
+               return UsageError(usage);
+       }
+       return nil;
+}
+
+/*
+ * Functions
+ */
+
+// defineFuncs populates world with the built-in functions.
+func defineFuncs() {
+       t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig);
+       world.DefineConst("Out", t, v);
+       t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig);
+       world.DefineConst("ContWait", t, v);
+       t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig);
+       world.DefineConst("BpSet", t, v);
+}
+
+// printCurFrame prints the current stack frame, as it would appear in
+// a backtrace.
+func printCurFrame() {
+       if curProc == nil || curProc.curGoroutine == nil {
+               return;
+       }
+       f := curProc.curGoroutine.frame;
+       if f == nil {
+               return;
+       }
+       fmt.Printf("=> %8x %v\n", f.pc, f);
+}
+
+// fnOut moves the current frame to the caller of the current frame.
+func fnOutSig() {}
+func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) {
+       if curProc == nil {
+               t.Abort(NoCurrentGoroutine{});
+       }
+       err := curProc.Out();
+       if err != nil {
+               t.Abort(err);
+       }
+       // TODO(austin) Only in the command form
+       printCurFrame();
+}
+
+// fnContWait continues the current process and waits for a stopping event.
+func fnContWaitSig() {}
+func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) {
+       if curProc == nil {
+               t.Abort(NoCurrentGoroutine{});
+       }
+       err := curProc.ContWait();
+       if err != nil {
+               t.Abort(err);
+       }
+       // TODO(austin) Only in the command form
+       ev := curProc.Event();
+       if ev != nil {
+               fmt.Printf("%v\n", ev);
+       }
+       printCurFrame();
+}
+
+// fnBpSet sets a breakpoint at the entry to the named function.
+func fnBpSetSig(string) {}
+func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) {
+       // TODO(austin) This probably shouldn't take a symbol name.
+       // Perhaps it should take an interface that provides PC's.
+       // Functions and instructions can implement that interface and
+       // we can have something to translate file:line pairs.
+       if curProc == nil {
+               t.Abort(NoCurrentGoroutine{});
+       }
+       name := args[0].(eval.StringValue).Get(t);
+       s := curProc.syms.SymFromName(name);
+       if s == nil {
+               t.Abort(UsageError("symbol " + name + " not defined"));
+       }
+       fn, ok := s.(*sym.TextSym);
+       if !ok {
+               t.Abort(UsageError("symbol " + name + " is not a function"));
+       }
+       curProc.OnBreakpoint(ptrace.Word(fn.Entry())).AddHandler(EventStop);
+}
index b3cc827b73006c0b1cd1dc14c8d37fefb7b89cf3..2dc3d7ec7b91f6f3be5780f55fff10c748e52734 100644 (file)
@@ -41,7 +41,6 @@ func (t *Goroutine) resetFrame() (err os.Error) {
 
 // Out selects the caller frame of the current frame.
 func (t *Goroutine) Out() os.Error {
-       // TODO(austin) Outer can abort
        f, err := t.frame.Outer();
        if f != nil {
                t.frame = f;
diff --git a/usr/austin/ogle/main.go b/usr/austin/ogle/main.go
new file mode 100644 (file)
index 0000000..7d94d38
--- /dev/null
@@ -0,0 +1,11 @@
+// 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 main
+
+import "ogle"
+
+func main() {
+       ogle.Main();
+}
index 7e4f3ac3d6309bc4e22a6bc4b0e5dd0db38eecab..e51fb15281fe909aa65169557ddae07afa472abf 100644 (file)
@@ -149,12 +149,18 @@ func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process,
        if err != nil {
                return nil, err;
        }
-       p.selectSomeGoroutine();
 
        // Create internal breakpoints to catch new and exited goroutines
        p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
        p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
 
+       // Select current frames
+       for _, g := range p.goroutines {
+               g.resetFrame();
+       }
+
+       p.selectSomeGoroutine();
+
        return p, nil;
 }
 
@@ -243,9 +249,9 @@ func (p *Process) selectSomeGoroutine() {
        // Once we have friendly goroutine ID's, there might be a more
        // reasonable behavior for this.
        p.curGoroutine = nil;
-       for _, t := range p.goroutines {
-               if !t.isG0() {
-                       p.curGoroutine = t;
+       for _, g := range p.goroutines {
+               if !g.isG0() && g.frame != nil {
+                       p.curGoroutine = g;
                        return;
                }
        }
@@ -486,8 +492,8 @@ func (p *Process) ContWait() os.Error {
                if err != nil {
                        return err;
                }
-               for _, t := range p.goroutines {
-                       t.resetFrame();
+               for _, g := range p.goroutines {
+                       g.resetFrame();
                }
                p.pending, err = p.causesToEvents();
                if err != nil {