blob: c9d599d6249d71cc41262ebeb2daa5b281ed1482 [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.
package probe
import (
"bufio"
"io"
"net"
"os"
"testing"
"unsafe"
"golang.org/x/debug/ogle/socket"
)
// traceThisFunction turns on tracing and returns a function to turn it off.
// It is intended for use as "defer traceThisFunction()()".
func traceThisFunction() func() {
// TODO: This should be done atomically to guarantee the probe can see the update.
tracing = true
return func() {
tracing = false
}
}
type Conn struct {
conn net.Conn
input *bufio.Reader
output *bufio.Writer
}
func (c *Conn) close() {
c.output.Flush()
c.conn.Close()
}
// newConn makes a connection.
func newConn(t *testing.T) *Conn {
// defer traceThisFunction()()
<-listening
conn, err := socket.Dial(os.Getuid(), os.Getpid())
if err != nil {
t.Fatal(err)
}
return &Conn{
conn: conn,
input: bufio.NewReader(conn),
output: bufio.NewWriter(conn),
}
}
// bytesToUint64 returns the uint64 stored in the 8 bytes of buf.
func bytesToUint64(buf []byte) uint64 {
// We're using same machine here, so byte order is the same.
// We can just fetch it, but on some architectures it
// must be aligned so copy first.
var tmp [8]byte
copy(tmp[:], buf)
return *(*uint64)(unsafe.Pointer(&tmp[0]))
}
// Test that we get an error back for a request to read an illegal address.
func TestReadBadAddress(t *testing.T) {
//defer traceThisFunction()()
conn := newConn(t)
defer conn.close()
// Read the elements in pseudo-random order.
var tmp [100]byte
// Request a read of a bad address.
conn.output.WriteByte('r')
// Address.
n := putUvarint(tmp[:], 0)
conn.output.Write(tmp[:n])
// Length. Any length will do.
n = putUvarint(tmp[:], 8)
conn.output.Write(tmp[:n])
// Send it.
err := conn.output.Flush()
if err != nil {
t.Fatal(err)
}
// Read result.
// We expect an initial non-zero value, the number of bytes of the error message.
u, err := readUvarint(conn.input)
if err != nil {
t.Fatal(err)
}
if u == 0 {
t.Fatalf("expected error return; got none")
}
// We expect a particular error.
const expect = "invalid read address"
if u != uint64(len(expect)) {
t.Fatalf("got %d bytes; expected %d", u, len(expect))
}
_, err = io.ReadFull(conn.input, tmp[:u])
if err != nil {
t.Fatal(err)
}
msg := string(tmp[:u])
if msg != expect {
t.Fatalf("got %q; expected %q", msg, expect)
}
}
// Test that we can read some data from the address space on the other side of the connection.
func TestReadUint64(t *testing.T) {
//defer traceThisFunction()()
conn := newConn(t)
defer conn.close()
// Some data to send over the wire.
data := make([]uint64, 10)
for i := range data {
data[i] = 0x1234567887654321 + 12345*uint64(i)
}
// TODO: To be righteous we should put a memory barrier here.
// Read the elements in pseudo-random order.
var tmp [100]byte
which := 0
for i := 0; i < 100; i++ {
which = (which + 7) % len(data)
// Request a read of data[which].
conn.output.WriteByte('r')
// Address.
n := putUvarint(tmp[:], uint64(addr(&data[which])))
conn.output.Write(tmp[:n])
// Length
n = putUvarint(tmp[:], 8)
conn.output.Write(tmp[:n])
// Send it.
err := conn.output.Flush()
if err != nil {
t.Fatal(err)
}
// Read result.
// We expect 10 bytes: the initial zero, followed by 8 (the count), followed by 8 bytes of data.
u, err := readUvarint(conn.input)
if err != nil {
t.Fatal(err)
}
if u != 0 {
t.Fatalf("expected leading zero byte; got %#x\n", u)
}
// N bytes of data.
u, err = readUvarint(conn.input)
if err != nil {
t.Fatal(err)
}
if u != 8 {
t.Fatalf("got %d bytes of data; expected 8", u)
}
_, err = io.ReadFull(conn.input, tmp[:u])
if err != nil {
t.Fatal(err)
}
u = bytesToUint64(tmp[:u])
if u != data[which] {
t.Fatalf("got %#x; expected %#x", u, data[which])
}
}
}
// Test that we can read an array bigger than the pipe's buffer size.
func TestBigRead(t *testing.T) {
// defer traceThisFunction()()
conn := newConn(t)
defer conn.close()
// A big slice.
data := make([]byte, 3*len(pipe{}.buf))
noise := 17
for i := range data {
data[i] = byte(noise)
noise += 23
}
// TODO: To be righteous we should put a memory barrier here.
// Read the elements in one big call.
tmp := make([]byte, len(data))
conn.output.WriteByte('r')
// Address.
n := putUvarint(tmp[:], uint64(addr(&data[0])))
conn.output.Write(tmp[:n])
// Length.
n = putUvarint(tmp[:], uint64(len(data)))
conn.output.Write(tmp[:n])
// Send it.
err := conn.output.Flush()
if err != nil {
t.Fatal(err)
}
// Read result.
// We expect the full data back.
u, err := readUvarint(conn.input)
if err != nil {
t.Fatal(err)
}
if u != 0 {
t.Fatalf("expected leading zero byte; got %#x\n", u)
}
// N bytes of data.
u, err = readUvarint(conn.input)
if err != nil {
t.Fatal(err)
}
if u != uint64(len(data)) {
t.Fatalf("got %d bytes of data; expected 8", u)
}
_, err = io.ReadFull(conn.input, tmp)
if err != nil {
t.Fatal(err)
}
for i, c := range data {
if tmp[i] != c {
t.Fatalf("at offset %d expected %#x; got %#x", i, c, tmp[i])
}
}
}
// TestCollectGarbage doesn't actually test anything, but it does collect any
// garbage sockets that are no longer used. It is a courtesy for computers that
// run this test suite often.
func TestCollectGarbage(t *testing.T) {
socket.CollectGarbage()
}