os: replace non-portable Waitmsg with portable ProcessState
Use methods for key questions.
Provide access to non-portable pieces through portable methods.
Windows and Plan 9 updated.

R=golang-dev, bradfitz, bradfitz, r, dsymonds, rsc, iant, iant
CC=golang-dev
https://golang.org/cl/5673077
diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go
index 155c659..cd7cde2 100644
--- a/src/cmd/cgo/util.go
+++ b/src/cmd/cgo/util.go
@@ -56,11 +56,11 @@
 	<-c
 	<-c
 
-	w, err := p.Wait()
+	state, err := p.Wait()
 	if err != nil {
 		fatalf("%s", err)
 	}
-	ok = w.Exited() && w.ExitStatus() == 0
+	ok = state.Success()
 	return
 }
 
diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go
index 80cf618..8d59220 100644
--- a/src/cmd/godoc/main.go
+++ b/src/cmd/godoc/main.go
@@ -109,10 +109,10 @@
 		log.Printf("os.Wait(%d, 0): %v", p.Pid, err)
 		return 2
 	}
-	status = wait.ExitStatus()
-	if !wait.Exited() || status > 1 {
+	if !wait.Success() {
 		os.Stderr.Write(buf.Bytes())
-		log.Printf("executing %v failed (exit status = %d)", args, status)
+		log.Printf("executing %v failed", args)
+		status = 1 // See comment in default case in dosync.
 		return
 	}
 
@@ -143,6 +143,8 @@
 		// don't change the package tree
 		syncDelay.set(time.Duration(*syncMin) * time.Minute) //  revert to regular sync schedule
 	default:
+		// TODO(r): this cannot happen now, since Wait has a boolean exit condition,
+		// not an integer.
 		// sync failed because of an error - back off exponentially, but try at least once a day
 		syncDelay.backoff(24 * time.Hour)
 	}
diff --git a/src/pkg/os/exec/exec.go b/src/pkg/os/exec/exec.go
index 248d97d..ebe92a9 100644
--- a/src/pkg/os/exec/exec.go
+++ b/src/pkg/os/exec/exec.go
@@ -79,9 +79,9 @@
 	// Process is the underlying process, once started.
 	Process *os.Process
 
-	// Waitmsg contains information about an exited process,
+	// ProcessState contains information about an exited process,
 	// available after a call to Wait or Run.
-	Waitmsg *os.Waitmsg
+	ProcessState *os.ProcessState
 
 	err             error // last error (from LookPath, stdin, stdout, stderr)
 	finished        bool  // when Wait was called
@@ -266,11 +266,11 @@
 
 // An ExitError reports an unsuccessful exit by a command.
 type ExitError struct {
-	*os.Waitmsg
+	*os.ProcessState
 }
 
 func (e *ExitError) Error() string {
-	return e.Waitmsg.String()
+	return e.ProcessState.String()
 }
 
 // Wait waits for the command to exit.
@@ -291,8 +291,8 @@
 		return errors.New("exec: Wait was already called")
 	}
 	c.finished = true
-	msg, err := c.Process.Wait()
-	c.Waitmsg = msg
+	state, err := c.Process.Wait()
+	c.ProcessState = state
 
 	var copyError error
 	for _ = range c.goroutine {
@@ -307,8 +307,8 @@
 
 	if err != nil {
 		return err
-	} else if !msg.Exited() || msg.ExitStatus() != 0 {
-		return &ExitError{msg}
+	} else if !state.Success() {
+		return &ExitError{state}
 	}
 
 	return copyError
diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go
index 92126c1..9da86e2 100644
--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -8,6 +8,7 @@
 	"errors"
 	"runtime"
 	"syscall"
+	"time"
 )
 
 // StartProcess starts a new process with the program, arguments and attributes
@@ -64,14 +65,9 @@
 	return e
 }
 
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
-	syscall.Waitmsg
-}
-
 // Wait waits for the Process to exit or stop, and then returns a
 // Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+func (p *Process) Wait() (ps *ProcessState, err error) {
 	var waitmsg syscall.Waitmsg
 
 	if p.Pid == -1 {
@@ -91,7 +87,11 @@
 		}
 	}
 
-	return &Waitmsg{waitmsg}, nil
+	ps = &ProcessState{
+		pid:    waitmsg.Pid,
+		status: waitmsg,
+	}
+	return ps, nil
 }
 
 // Release releases any resources associated with the Process.
@@ -108,9 +108,57 @@
 	return newProcess(pid, 0), nil
 }
 
-func (w *Waitmsg) String() string {
-	if w == nil {
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+	pid    int             // The process's id.
+	status syscall.Waitmsg // System-dependent status info.
+}
+
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+	return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+	return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+	return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process.  Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Plan 9, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+	return &p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process.  Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+	return &p.status
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+	return time.Duration(p.status.Time[0]) * time.Millisecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+	return time.Duration(p.status.Time[1]) * time.Millisecond
+}
+
+func (p *ProcessState) String() string {
+	if p == nil {
 		return "<nil>"
 	}
-	return "exit status: " + w.Msg
+	return "exit status: " + p.status.Msg
 }
diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go
index 03c7f0e..2b8d2b2 100644
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -42,18 +42,41 @@
 	return p.Signal(Kill)
 }
 
-// TODO(rsc): Should os implement its own syscall.WaitStatus
-// wrapper with the methods, or is exposing the underlying one enough?
-//
-// TODO(rsc): Certainly need to have Rusage struct,
-// since syscall one might have different field types across
-// different OS.
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+	pid    int                 // The process's id.
+	status *syscall.WaitStatus // System-dependent status info.
+	rusage *syscall.Rusage
+}
 
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
-	Pid                int             // The process's id.
-	syscall.WaitStatus                 // System-dependent status info.
-	Rusage             *syscall.Rusage // System-dependent resource usage info.
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+	return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+	return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+	return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process.  Convert it to the appropriate underlying
+// type, such as *syscall.WaitStatus on Unix, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+	return p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process.  Convert it to the appropriate underlying
+// type, such as *syscall.Rusage on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+	return p.rusage
 }
 
 // Convert i to decimal string.
@@ -83,26 +106,26 @@
 	return string(b[bp:])
 }
 
-func (w *Waitmsg) String() string {
-	if w == nil {
+func (p *ProcessState) String() string {
+	if p == nil {
 		return "<nil>"
 	}
-	// TODO(austin) Use signal names when possible?
+	status := p.Sys().(*syscall.WaitStatus)
 	res := ""
 	switch {
-	case w.Exited():
-		res = "exit status " + itod(w.ExitStatus())
-	case w.Signaled():
-		res = "signal " + itod(int(w.Signal()))
-	case w.Stopped():
-		res = "stop signal " + itod(int(w.StopSignal()))
-		if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
-			res += " (trap " + itod(w.TrapCause()) + ")"
+	case status.Exited():
+		res = "exit status " + itod(status.ExitStatus())
+	case status.Signaled():
+		res = "signal " + itod(int(status.Signal()))
+	case status.Stopped():
+		res = "stop signal " + itod(int(status.StopSignal()))
+		if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
+			res += " (trap " + itod(status.TrapCause()) + ")"
 		}
-	case w.Continued():
+	case status.Continued():
 		res = "continued"
 	}
-	if w.CoreDump() {
+	if status.CoreDump() {
 		res += " (core dumped)"
 	}
 	return res
diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go
index b9880ff..e5905f0 100644
--- a/src/pkg/os/exec_unix.go
+++ b/src/pkg/os/exec_unix.go
@@ -10,26 +10,30 @@
 	"errors"
 	"runtime"
 	"syscall"
+	"time"
 )
 
 // Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
 	if p.Pid == -1 {
 		return nil, syscall.EINVAL
 	}
 	var status syscall.WaitStatus
-	pid1, e := syscall.Wait4(p.Pid, &status, 0, nil)
+	var rusage syscall.Rusage
+	pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
 	if e != nil {
 		return nil, NewSyscallError("wait", e)
 	}
 	if pid1 != 0 {
 		p.done = true
 	}
-	w = new(Waitmsg)
-	w.Pid = pid1
-	w.WaitStatus = status
-	return w, nil
+	ps = &ProcessState{
+		pid:    pid1,
+		status: &status,
+		rusage: &rusage,
+	}
+	return ps, nil
 }
 
 // Signal sends a signal to the Process.
@@ -60,3 +64,13 @@
 	// NOOP for unix.
 	return newProcess(pid, 0), nil
 }
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+	return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+	return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
+}
diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go
index 7d46c89..8887ba4 100644
--- a/src/pkg/os/exec_windows.go
+++ b/src/pkg/os/exec_windows.go
@@ -8,12 +8,13 @@
 	"errors"
 	"runtime"
 	"syscall"
+	"time"
 	"unsafe"
 )
 
 // Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any.
-func (p *Process) Wait() (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
 	s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE)
 	switch s {
 	case syscall.WAIT_OBJECT_0:
@@ -29,7 +30,7 @@
 		return nil, NewSyscallError("GetExitCodeProcess", e)
 	}
 	p.done = true
-	return &Waitmsg{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
+	return &ProcessState{p.Pid, &syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
 }
 
 // Signal sends a signal to the Process.
@@ -83,3 +84,15 @@
 		Args[i] = string(syscall.UTF16ToString((*v)[:]))
 	}
 }
+
+// UserTime returns the user CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+	return 0
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+	return 0
+}
diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 976d64b..21e2f37 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -1007,10 +1007,10 @@
 	}
 }
 
-func TestNilWaitmsgString(t *testing.T) {
-	var w *Waitmsg
-	s := w.String()
+func TestNilProcessStateString(t *testing.T) {
+	var ps *ProcessState
+	s := ps.String()
 	if s != "<nil>" {
-		t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>")
+		t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
 	}
 }