// 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;
}
