libgo: Remove debug/proc, os.Error.
From-SVN: r182074
This commit is contained in:
parent
9c63abc9a1
commit
598fd331d0
7 changed files with 3 additions and 395 deletions
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2011 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 proc
|
||||
|
||||
import "os"
|
||||
|
||||
// Process tracing is not supported on IRIX yet.
|
||||
|
||||
func Attach(pid int) (Process, os.Error) {
|
||||
return nil, os.NewError("debug/proc not implemented on IRIX")
|
||||
}
|
||||
|
||||
func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
|
||||
return Attach(0)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2011 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 proc
|
||||
|
||||
import "os"
|
||||
|
||||
// Process tracing is not supported on RTEMS yet.
|
||||
|
||||
func Attach(pid int) (Process, os.Error) {
|
||||
return nil, os.NewError("debug/proc not implemented on RTEMS")
|
||||
}
|
||||
|
||||
func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
|
||||
return Attach(0)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright 2011 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 proc
|
||||
|
||||
import "os"
|
||||
|
||||
// Process tracing is not supported on Solaris yet.
|
||||
|
||||
func Attach(pid int) (Process, os.Error) {
|
||||
return nil, os.NewError("debug/proc not implemented on Solaris")
|
||||
}
|
||||
|
||||
func ForkExec(argv0 string, argv []string, envv []string, dir string, fd []*os.File) (Process, os.Error) {
|
||||
return Attach(0)
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
// 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.
|
||||
|
||||
ptrace and NTPL, the missing manpage
|
||||
|
||||
== Signals ==
|
||||
|
||||
A signal sent to a ptrace'd process or thread causes only the thread
|
||||
that receives it to stop and report to the attached process.
|
||||
|
||||
Use tgkill to target a signal (for example, SIGSTOP) at a particular
|
||||
thread. If you use kill, the signal could be delivered to another
|
||||
thread in the same process.
|
||||
|
||||
Note that SIGSTOP differs from its usual behavior when a process is
|
||||
being traced. Usually, a SIGSTOP sent to any thread in a thread group
|
||||
will stop all threads in the thread group. When a thread is traced,
|
||||
however, a SIGSTOP affects only the receiving thread (and any other
|
||||
threads in the thread group that are not traced).
|
||||
|
||||
SIGKILL behaves like it does for non-traced processes. It affects all
|
||||
threads in the process and terminates them without the WSTOPSIG event
|
||||
generated by other signals. However, if PTRACE_O_TRACEEXIT is set,
|
||||
the attached process will still receive PTRACE_EVENT_EXIT events
|
||||
before receiving WIFSIGNALED events.
|
||||
|
||||
See "Following thread death" for a caveat regarding signal delivery to
|
||||
zombie threads.
|
||||
|
||||
== Waiting on threads ==
|
||||
|
||||
Cloned threads in ptrace'd processes are treated similarly to cloned
|
||||
threads in your own process. Thus, you must use the __WALL option in
|
||||
order to receive notifications from threads created by the child
|
||||
process. Similarly, the __WCLONE option will wait only on
|
||||
notifications from threads created by the child process and *not* on
|
||||
notifications from the initial child thread.
|
||||
|
||||
Even when waiting on a specific thread's PID using waitpid or similar,
|
||||
__WALL or __WCLONE is necessary or waitpid will return ECHILD.
|
||||
|
||||
== Attaching to existing threads ==
|
||||
|
||||
libthread_db (which gdb uses), attaches to existing threads by pulling
|
||||
the pthread data structures out of the traced process. The much
|
||||
easier way is to traverse the /proc/PID/task directory, though it's
|
||||
unclear how the semantics of these two approaches differ.
|
||||
|
||||
Unfortunately, if the main thread has exited (but the overall process
|
||||
has not), it sticks around as a zombie process. This zombie will
|
||||
appear in the /proc/PID/task directory, but trying to attach to it
|
||||
will yield EPERM. In this case, the third field of the
|
||||
/proc/PID/task/PID/stat file will be "Z". Attempting to open the stat
|
||||
file is also a convenient way to detect races between listing the task
|
||||
directory and the thread exiting. Coincidentally, gdb will simply
|
||||
fail to attach to a process whose main thread is a zombie.
|
||||
|
||||
Because new threads may be created while the debugger is in the
|
||||
process of attaching to existing threads, the debugger must repeatedly
|
||||
re-list the task directory until it has attached to (and thus stopped)
|
||||
every thread listed.
|
||||
|
||||
In order to follow new threads created by existing threads,
|
||||
PTRACE_O_TRACECLONE must be set on each thread attached to.
|
||||
|
||||
== Following new threads ==
|
||||
|
||||
With the child process stopped, use PTRACE_SETOPTIONS to set the
|
||||
PTRACE_O_TRACECLONE option. This option is per-thread, and thus must
|
||||
be set on each existing thread individually. When an existing thread
|
||||
with PTRACE_O_TRACECLONE set spawns a new thread, the existing thread
|
||||
will stop with (SIGTRAP | PTRACE_EVENT_CLONE << 8) and the PID of the
|
||||
new thread can be retrieved with PTRACE_GETEVENTMSG on the creating
|
||||
thread. At this time, the new thread will exist, but will initially
|
||||
be stopped with a SIGSTOP. The new thread will automatically be
|
||||
traced and will inherit the PTRACE_O_TRACECLONE option from its
|
||||
parent. The attached process should wait on the new thread to receive
|
||||
the SIGSTOP notification.
|
||||
|
||||
When using waitpid(-1, ...), don't rely on the parent thread reporting
|
||||
a SIGTRAP before receiving the SIGSTOP from the new child thread.
|
||||
|
||||
Without PTRACE_O_TRACECLONE, newly cloned threads will not be
|
||||
ptrace'd. As a result, signals received by new threads will be
|
||||
handled in the usual way, which may affect the parent and in turn
|
||||
appear to the attached process, but attributed to the parent (possibly
|
||||
in unexpected ways).
|
||||
|
||||
== Following thread death ==
|
||||
|
||||
If any thread with the PTRACE_O_TRACEEXIT option set exits (either by
|
||||
returning or pthread_exit'ing), the tracing process will receive an
|
||||
immediate PTRACE_EVENT_EXIT. At this point, the thread will still
|
||||
exist. The exit status, encoded as for wait, can be queried using
|
||||
PTRACE_GETEVENTMSG on the exiting thread's PID. The thread should be
|
||||
continued so it can actually exit, after which its wait behavior is
|
||||
the same as for a thread without the PTRACE_O_TRACEEXIT option.
|
||||
|
||||
If a non-main thread exits (either by returning or pthread_exit'ing),
|
||||
its corresponding process will also exit, producing a WIFEXITED event
|
||||
(after the process is continued from a possible PTRACE_EVENT_EXIT
|
||||
event). It is *not* necessary for another thread to ptrace_join for
|
||||
this to happen.
|
||||
|
||||
If the main thread exits by returning, then all threads will exit,
|
||||
first generating a PTRACE_EVENT_EXIT event for each thread if
|
||||
appropriate, then producing a WIFEXITED event for each thread.
|
||||
|
||||
If the main thread exits using pthread_exit, then it enters a
|
||||
non-waitable zombie state. It will still produce an immediate
|
||||
PTRACE_O_TRACEEXIT event, but the WIFEXITED event will be delayed
|
||||
until the entire process exits. This state exists so that shells
|
||||
don't think the process is done until all of the threads have exited.
|
||||
Unfortunately, signals cannot be delivered to non-waitable zombies.
|
||||
Most notably, SIGSTOP cannot be delivered; as a result, when you
|
||||
broadcast SIGSTOP to all of the threads, you must not wait for
|
||||
non-waitable zombies to stop. Furthermore, any ptrace command on a
|
||||
non-waitable zombie, including PTRACE_DETACH, will return ESRCH.
|
||||
|
||||
== Multi-threaded debuggers ==
|
||||
|
||||
If the debugger itself is multi-threaded, ptrace calls must come from
|
||||
the same thread that originally attached to the remote thread. The
|
||||
kernel simply compares the PID of the caller of ptrace against the
|
||||
tracer PID of the process passed to ptrace. Because each debugger
|
||||
thread has a different PID, calling ptrace from a different thread
|
||||
might as well be calling it from a different process and the kernel
|
||||
will return ESRCH.
|
||||
|
||||
wait, on the other hand, does not have this restriction. Any debugger
|
||||
thread can wait on any thread in the attached process.
|
|
@ -1,209 +0,0 @@
|
|||
// Copyright 2011 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 proc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type alphaRegs struct {
|
||||
syscall.PtraceRegs
|
||||
setter func(*syscall.PtraceRegs) os.Error
|
||||
}
|
||||
|
||||
var names = [...]string{
|
||||
"r0",
|
||||
"r1",
|
||||
"r2",
|
||||
"r3",
|
||||
"r4",
|
||||
"r5",
|
||||
"r6",
|
||||
"r7",
|
||||
"r8",
|
||||
"r19",
|
||||
"r20",
|
||||
"r21",
|
||||
"r22",
|
||||
"r23",
|
||||
"r24",
|
||||
"r25",
|
||||
"r26",
|
||||
"r27",
|
||||
"r28",
|
||||
"hae",
|
||||
"trap_a0",
|
||||
"trap_a1",
|
||||
"trap_a2",
|
||||
"ps",
|
||||
"pc",
|
||||
"gp",
|
||||
"r16",
|
||||
"r17",
|
||||
"r18",
|
||||
}
|
||||
|
||||
func (r *alphaRegs) PC() Word { return Word(r.Pc) }
|
||||
|
||||
func (r *alphaRegs) SetPC(val Word) os.Error {
|
||||
r.Pc = uint64(val)
|
||||
return r.setter(&r.PtraceRegs)
|
||||
}
|
||||
|
||||
func (r *alphaRegs) Link() Word {
|
||||
panic("No link register")
|
||||
}
|
||||
|
||||
func (r *alphaRegs) SetLink(val Word) os.Error {
|
||||
panic("No link register")
|
||||
}
|
||||
|
||||
func (r *alphaRegs) SP() Word { return Word(r.Ps) }
|
||||
|
||||
func (r *alphaRegs) SetSP(val Word) os.Error {
|
||||
r.Ps = uint64(val)
|
||||
return r.setter(&r.PtraceRegs)
|
||||
}
|
||||
|
||||
func (r *alphaRegs) Names() []string { return names[0:] }
|
||||
|
||||
func (r *alphaRegs) Get(i int) Word {
|
||||
switch i {
|
||||
case 0:
|
||||
return Word(r.R0)
|
||||
case 1:
|
||||
return Word(r.R1)
|
||||
case 2:
|
||||
return Word(r.R2)
|
||||
case 3:
|
||||
return Word(r.R3)
|
||||
case 4:
|
||||
return Word(r.R4)
|
||||
case 5:
|
||||
return Word(r.R5)
|
||||
case 6:
|
||||
return Word(r.R6)
|
||||
case 7:
|
||||
return Word(r.R7)
|
||||
case 8:
|
||||
return Word(r.R8)
|
||||
case 9:
|
||||
return Word(r.R19)
|
||||
case 10:
|
||||
return Word(r.R20)
|
||||
case 11:
|
||||
return Word(r.R21)
|
||||
case 12:
|
||||
return Word(r.R22)
|
||||
case 13:
|
||||
return Word(r.R23)
|
||||
case 14:
|
||||
return Word(r.R24)
|
||||
case 15:
|
||||
return Word(r.R25)
|
||||
case 16:
|
||||
return Word(r.R26)
|
||||
case 17:
|
||||
return Word(r.R27)
|
||||
case 18:
|
||||
return Word(r.R28)
|
||||
case 19:
|
||||
return Word(r.Hae)
|
||||
case 20:
|
||||
return Word(r.Trap_a0)
|
||||
case 21:
|
||||
return Word(r.Trap_a1)
|
||||
case 22:
|
||||
return Word(r.Trap_a2)
|
||||
case 23:
|
||||
return Word(r.Ps)
|
||||
case 24:
|
||||
return Word(r.Pc)
|
||||
case 25:
|
||||
return Word(r.Gp)
|
||||
case 26:
|
||||
return Word(r.R16)
|
||||
case 27:
|
||||
return Word(r.R17)
|
||||
case 28:
|
||||
return Word(r.R18)
|
||||
}
|
||||
panic("invalid register index " + strconv.Itoa(i))
|
||||
}
|
||||
|
||||
func (r *alphaRegs) Set(i int, val Word) os.Error {
|
||||
switch i {
|
||||
case 0:
|
||||
r.R0 = uint64(val)
|
||||
case 1:
|
||||
r.R1 = uint64(val)
|
||||
case 2:
|
||||
r.R2 = uint64(val)
|
||||
case 3:
|
||||
r.R3 = uint64(val)
|
||||
case 4:
|
||||
r.R4 = uint64(val)
|
||||
case 5:
|
||||
r.R5 = uint64(val)
|
||||
case 6:
|
||||
r.R6 = uint64(val)
|
||||
case 7:
|
||||
r.R7 = uint64(val)
|
||||
case 8:
|
||||
r.R8 = uint64(val)
|
||||
case 9:
|
||||
r.R19 = uint64(val)
|
||||
case 10:
|
||||
r.R20 = uint64(val)
|
||||
case 11:
|
||||
r.R21 = uint64(val)
|
||||
case 12:
|
||||
r.R22 = uint64(val)
|
||||
case 13:
|
||||
r.R23 = uint64(val)
|
||||
case 14:
|
||||
r.R24 = uint64(val)
|
||||
case 15:
|
||||
r.R25 = uint64(val)
|
||||
case 16:
|
||||
r.R26 = uint64(val)
|
||||
case 17:
|
||||
r.R27 = uint64(val)
|
||||
case 18:
|
||||
r.R28 = uint64(val)
|
||||
case 19:
|
||||
r.Hae = uint64(val)
|
||||
case 20:
|
||||
r.Trap_a0 = uint64(val)
|
||||
case 21:
|
||||
r.Trap_a1 = uint64(val)
|
||||
case 22:
|
||||
r.Trap_a2 = uint64(val)
|
||||
case 23:
|
||||
r.Ps = uint64(val)
|
||||
case 24:
|
||||
r.Pc = uint64(val)
|
||||
case 25:
|
||||
r.Gp = uint64(val)
|
||||
case 26:
|
||||
r.R16 = uint64(val)
|
||||
case 27:
|
||||
r.R17 = uint64(val)
|
||||
case 28:
|
||||
r.R18 = uint64(val)
|
||||
default:
|
||||
panic("invalid register index " + strconv.Itoa(i))
|
||||
}
|
||||
return r.setter(&r.PtraceRegs)
|
||||
}
|
||||
|
||||
func newRegs(regs *syscall.PtraceRegs, setter func(*syscall.PtraceRegs) os.Error) Regs {
|
||||
res := alphaRegs{}
|
||||
res.PtraceRegs = *regs
|
||||
res.setter = setter
|
||||
return &res
|
||||
}
|
|
@ -9,7 +9,7 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
func selfConnectedTCPSocket() (pr, pw *os.File, err os.Error) {
|
||||
func selfConnectedTCPSocket() (pr, pw *os.File, err error) {
|
||||
// See ../syscall/exec.go for description of ForkLock.
|
||||
syscall.ForkLock.RLock()
|
||||
sockfd, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
|
||||
|
@ -49,7 +49,7 @@ func selfConnectedTCPSocket() (pr, pw *os.File, err os.Error) {
|
|||
return fd, fd, nil
|
||||
}
|
||||
|
||||
func newPollServer() (s *pollServer, err os.Error) {
|
||||
func newPollServer() (s *pollServer, err error) {
|
||||
s = new(pollServer)
|
||||
s.cr = make(chan *netFD, 1)
|
||||
s.cw = make(chan *netFD, 1)
|
||||
|
|
|
@ -37,7 +37,7 @@ var elen int;
|
|||
// If n <= 0, Readdirnames returns all the names from the directory in
|
||||
// a single slice. In this case, if Readdirnames succeeds (reads all
|
||||
// the way to the end of the directory), it returns the slice and a
|
||||
// nil os.Error. If it encounters an error before the end of the
|
||||
// nil error. If it encounters an error before the end of the
|
||||
// directory, Readdirnames returns the names read until that point and
|
||||
// a non-nil error.
|
||||
func (file *File) Readdirnames(n int) (names []string, err error) {
|
||||
|
|
Loading…
Add table
Reference in a new issue