| // 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() |
| } |