blob: 87ebe98b819eb73d4ff17c6aed7e3b15b73c54fa [file] [log] [blame]
// Copyright 2014 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.
// TODO: Document the protocol once it settles.
package probe
import (
"errors"
"io" // Used only for the definitions of the various interfaces and errors.
"net"
"golang.org/x/debug/ogle/socket"
)
var (
tracing = false
listening = make(chan struct{})
)
// init starts a listener and leaves it in the background waiting for connections.
func init() {
go demon()
}
// demon answers consecutive connection requests and starts a server to manage each one.
// The server runs in the same goroutine as the demon, so a new connection cannot be
// established until the previous one is completed.
func demon() {
listener, err := socket.Listen()
close(listening)
if err != nil {
trace("listen:", err)
return
}
trace("listening")
for {
conn, err := listener.Accept()
if err != nil {
trace("accept", err)
continue
}
trace("accepted a connection")
serve(conn)
conn.Close()
}
}
// stringer is the same as fmt.Stringer. We redefine it here to avoid pulling in fmt.
type stringer interface {
String() string
}
func printHex(b byte) {
const hex = "0123456789ABCDEF"
b1, b0 := b>>4&0xF, b&0xF
print(hex[b1:b1+1], hex[b0:b0+1])
}
// trace is a simple version of println that is enabled by the tracing boolean.
func trace(args ...interface{}) {
if !tracing {
return
}
print("ogle demon: ")
for i, arg := range args {
if i > 0 {
print(" ")
}
// A little help. Built-in print isn't very capable.
switch arg := arg.(type) {
case stringer:
print(arg.String())
case error:
print(arg.Error())
case []byte:
print("[")
for i := range arg {
if i > 0 {
print(" ")
}
printHex(arg[i])
}
print("]")
case int:
print(arg)
case string:
print(arg)
case uintptr:
print("0x")
for i := ptrSize - 1; i >= 0; i-- {
printHex(byte(arg >> uint(8*i)))
}
default:
print(arg)
}
}
print("\n")
}
func serve(conn net.Conn) {
const (
bufSize = 1 << 16
)
var buf [bufSize]byte
network := &pipe{
rw: conn,
}
for {
// One message per loop.
n, err := network.Read(buf[:1])
if n != 1 || err != nil {
return
}
switch buf[0] {
case 'r':
// Read: ['r', address, size] => [0, size, size bytes]
u, err := network.readUintptr()
if err != nil {
return
}
n, err := network.readInt()
if err != nil {
return
}
if !validRead(u, n) {
trace("read", err)
network.error("invalid read address")
continue
}
network.sendReadResponse(u, n)
default:
// TODO: shut down connection?
trace("unknown message type:", buf[0])
}
}
}
// pipe is a buffered network connection (actually just a reader/writer) that
// implements Read and ReadByte as well as readFull.
// It also has support routines to make it easier to read and write
// network messages.
type pipe struct {
rw io.ReadWriter
pos int
end int
oneByte [1]byte
buf [4096]byte
}
// readFull fills the argument slice with data from the wire. If it cannot fill the
// slice, it returns an error.
// TODO: unused for now; write will need it.
func (p *pipe) readFull(buf []byte) error {
for len(buf) > 0 {
n, err := p.rw.Read(buf)
if n == len(buf) {
return nil
}
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
if n == 0 {
return io.EOF
}
buf = buf[n:]
}
return nil
}
// Read satisfies io.Reader.
func (p *pipe) Read(buf []byte) (int, error) {
n := len(buf)
if p.end == p.pos {
p.pos = 0
// Read from network
var err error
p.end, err = p.rw.Read(p.buf[:])
if err != nil {
trace("read:", err)
return p.end, err
}
if p.end == 0 {
trace("read: eof")
return p.end, io.EOF
}
}
if n > p.end-p.pos {
n = p.end - p.pos
}
copy(buf, p.buf[p.pos:p.pos+n])
p.pos += n
return n, nil
}
// ReadByte satisfies io.ByteReader.
func (p *pipe) ReadByte() (byte, error) {
_, err := p.Read(p.oneByte[:])
return p.oneByte[0], err
}
// readUintptr reads a varint-encoded uinptr value from the connection.
func (p *pipe) readUintptr() (uintptr, error) {
u, err := readUvarint(p)
if err != nil {
trace("read uintptr:", err)
return 0, err
}
if u > uint64(^uintptr(0)) {
trace("read uintptr: overflow")
return 0, err
}
return uintptr(u), nil
}
var intOverflow = errors.New("ogle probe: varint overflows int")
// readInt reads an varint-encoded int value from the connection.
// The transported value is always a uint64; this routine
// verifies that it fits in an int.
func (p *pipe) readInt() (int, error) {
u, err := readUvarint(p)
if err != nil {
trace("read int:", err)
return 0, err
}
// Does it fit in an int?
if u > maxInt {
trace("int overflow")
return 0, intOverflow
}
return int(u), nil
}
// error writes an error message to the connection.
// The format is [size, size bytes].
func (p *pipe) error(msg string) {
// A zero-length message is problematic. It should never arise, but be safe.
if len(msg) == 0 {
msg = "undefined error"
}
// Truncate if necessary. Extremely unlikely.
if len(msg) > len(p.buf)-maxVarintLen64 {
msg = msg[:len(p.buf)-maxVarintLen64]
}
n := putUvarint(p.buf[:], uint64(len(msg)))
n += copy(p.buf[n:], msg)
_, err := p.rw.Write(p.buf[:n])
if err != nil {
trace("write:", err)
// TODO: shut down connection?
}
}
// sendReadResponse sends a read response to the connection.
// The format is [0, size, size bytes].
func (p *pipe) sendReadResponse(addr uintptr, size int) {
trace("sendRead:", addr, size)
m := 0
m += putUvarint(p.buf[m:], 0) // No error.
m += putUvarint(p.buf[m:], uint64(size)) // Number of bytes to follow.
for m > 0 || size > 0 {
n := len(p.buf) - m
if n > size {
n = size
}
if !read(addr, p.buf[m:m+n]) {
trace("copy error")
// TODO: shut down connection?
// TODO: for now, continue delivering data. We said we would.
}
_, err := p.rw.Write(p.buf[:m+n])
if err != nil {
trace("write:", err)
// TODO: shut down connection?
}
addr += uintptr(n)
size -= n
// Next time we can use the whole buffer.
m = 0
}
}