// 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 ptrace provides a platform-independent interface for // tracing and controlling running processes. It supports // multi-threaded processes and provides typical low-level debugging // controls such as breakpoints, single stepping, and manipulating // memory and registers. package proc // TODO(rsc): Have to import everything that proc_linux.go // and proc_darwin.go do, because deps.bash only looks at // this file. import ( _ "container/vector"; _ "fmt"; _ "io"; "os"; _ "runtime"; "strconv"; _ "strings"; _ "sync"; _ "syscall"; ) type Word uint64 // A Cause explains why a thread is stopped. type Cause interface { String() string; } // Regs is a set of named machine registers, including a program // counter, link register, and stack pointer. // // TODO(austin) There's quite a proliferation of methods here. We // could make a Reg interface with Get and Set and make this just PC, // Link, SP, Names, and Reg. We could also put Index in Reg and that // makes it easy to get the index of things like the PC (currently // there's just no way to know that). This would also let us include // other per-register information like how to print it. type Regs interface { // PC returns the value of the program counter. PC() Word; // SetPC sets the program counter to val. SetPC(val Word) os.Error; // Link returns the link register, if any. Link() Word; // SetLink sets the link register to val. SetLink(val Word) os.Error; // SP returns the value of the stack pointer. SP() Word; // SetSP sets the stack pointer register to val. SetSP(val Word) os.Error; // Names returns the names of all of the registers. Names() []string; // Get returns the value of a register, where i corresponds to // the index of the register's name in the array returned by // Names. Get(i int) Word; // Set sets the value of a register. Set(i int, val Word) os.Error; } // Thread is a thread in the process being traced. type Thread interface { // Step steps this thread by a single instruction. The thread // must be stopped. If the thread is currently stopped on a // breakpoint, this will step over the breakpoint. // // XXX What if it's stopped because of a signal? Step() os.Error; // Stopped returns the reason that this thread is stopped. It // is an error is the thread not stopped. Stopped() (Cause, os.Error); // Regs retrieves the current register values from this // thread. The thread must be stopped. Regs() (Regs, os.Error); // Peek reads len(out) bytes from the address addr in this // thread into out. The thread must be stopped. It returns // the number of bytes successfully read. If an error occurs, // such as attempting to read unmapped memory, this count // could be short and an error will be returned. If this does // encounter unmapped memory, it will read up to the byte // preceding the unmapped area. Peek(addr Word, out []byte) (int, os.Error); // Poke writes b to the address addr in this thread. The // thread must be stopped. It returns the number of bytes // successfully written. If an error occurs, such as // attempting to write to unmapped memory, this count could be // short and an error will be returned. If this does // encounter unmapped memory, it will write up to the byte // preceding the unmapped area. Poke(addr Word, b []byte) (int, os.Error); } // Process is a process being traced. It consists of a set of // threads. A process can be running, stopped, or terminated. The // process's state extends to all of its threads. type Process interface { // Threads returns an array of all threads in this process. Threads() []Thread; // AddBreakpoint creates a new breakpoint at program counter // pc. Breakpoints can only be created when the process is // stopped. It is an error if a breakpoint already exists at // pc. AddBreakpoint(pc Word) os.Error; // RemoveBreakpoint removes the breakpoint at the program // counter pc. It is an error if no breakpoint exists at pc. RemoveBreakpoint(pc Word) os.Error; // Stop stops all running threads in this process before // returning. Stop() os.Error; // Continue resumes execution of all threads in this process. // Any thread that is stopped on a breakpoint will be stepped // over that breakpoint. Any thread that is stopped because // of a signal (other than SIGSTOP or SIGTRAP) will receive // the pending signal. Continue() os.Error; // WaitStop waits until all threads in process p are stopped // as a result of some thread hitting a breakpoint, receiving // a signal, creating a new thread, or exiting. WaitStop() os.Error; // Detach detaches from this process. All stopped threads // will be resumed. Detach() os.Error; } // Stopped is a stop cause used for threads that are stopped either by // user request (e.g., from the Stop method or after single stepping), // or that are stopped because some other thread caused the program to // stop. type Stopped struct{} func (c Stopped) String() string { return "stopped" } // Breakpoint is a stop cause resulting from a thread reaching a set // breakpoint. type Breakpoint Word // PC returns the program counter that the program is stopped at. func (c Breakpoint) PC() Word { return Word(c) } func (c Breakpoint) String() string { return "breakpoint at 0x" + strconv.Uitob64(uint64(c.PC()), 16) } // Signal is a stop cause resulting from a thread receiving a signal. // When the process is continued, the signal will be delivered. type Signal string // Signal returns the signal being delivered to the thread. func (c Signal) Name() string { return string(c) } func (c Signal) String() string { return c.Name() } // ThreadCreate is a stop cause returned from an existing thread when // it creates a new thread. The new thread exists in a primordial // form at this point and will begin executing in earnest when the // process is continued. type ThreadCreate struct { thread Thread; } func (c *ThreadCreate) NewThread() Thread { return c.thread } func (c *ThreadCreate) String() string { return "thread create" } // ThreadExit is a stop cause resulting from a thread exiting. When // this cause first arises, the thread will still be in the list of // process threads and its registers and memory will still be // accessible. type ThreadExit struct { exitStatus int; signal string; } // Exited returns true if the thread exited normally. func (c *ThreadExit) Exited() bool { return c.exitStatus != -1 } // ExitStatus returns the exit status of the thread if it exited // normally or -1 otherwise. func (c *ThreadExit) ExitStatus() int { return c.exitStatus } // Signaled returns true if the thread was terminated by a signal. func (c *ThreadExit) Signaled() bool { return c.exitStatus == -1 } // StopSignal returns the signal that terminated the thread, or "" if // it was not terminated by a signal. func (c *ThreadExit) StopSignal() string { return c.signal } func (c *ThreadExit) String() string { res := "thread exited "; switch { case c.Exited(): res += "with status " + strconv.Itoa(c.ExitStatus()) case c.Signaled(): res += "from signal " + c.StopSignal() default: res += "from unknown cause" } return res; }