buildbot: use ringBuffer to store last 1MB of logs

Change-Id: I689ca82c64b9c7f89913551c5138a78718377c0d
This commit is contained in:
Bill Thiede 2013-10-10 20:59:29 -07:00
parent 72021e4516
commit 0712432e1f
2 changed files with 95 additions and 7 deletions

View File

@ -40,9 +40,10 @@ import (
)
const (
interval = 60 * time.Second // polling frequency
warmup = 60 * time.Second // duration before we test if devcam server has started properly
historySize = 30
interval = 60 * time.Second // polling frequency
warmup = 60 * time.Second // duration before we test if devcam server has started properly
historySize = 30
maxStderrSize = 1 << 20 // Keep last 1 MB of logging.
)
var (
@ -89,21 +90,67 @@ var (
// Override the os.Stderr used by the default logger so we can provide
// more debug info on status page.
logStderr = new(lockedBuffer)
logStderr = newLockedBuffer()
)
// lockedBuffer protects all Write calls with a mutex. Users of lockedBuffer
// must wrap any calls to Bytes, and use of the resulting slice with calls to
// Lock/Unlock.
type lockedBuffer struct {
sync.Mutex // guards Buffer
bytes.Buffer
sync.Mutex // guards ringBuffer
*ringBuffer
}
func newLockedBuffer() *lockedBuffer {
return &lockedBuffer{ringBuffer: newRingBuffer(maxStderrSize)}
}
func (lb *lockedBuffer) Write(b []byte) (int, error) {
lb.Lock()
defer lb.Unlock()
return lb.Buffer.Write(b)
return lb.ringBuffer.Write(b)
}
type ringBuffer struct {
buf []byte
off int // End of ring buffer.
l int // Length of ring buffer filled.
}
func newRingBuffer(maxSize int) *ringBuffer {
return &ringBuffer{
buf: make([]byte, maxSize),
}
}
func (rb *ringBuffer) Bytes() []byte {
if (rb.off - rb.l) >= 0 {
// Partially full buffer with no wrap.
return rb.buf[rb.off-rb.l : rb.off]
}
// Buffer has been wrapped, copy second half then first half.
start := rb.off - rb.l
if start < 0 {
start = rb.off
}
b := make([]byte, 0, cap(rb.buf))
b = append(b, rb.buf[start:]...)
b = append(b, rb.buf[:start]...)
return b
}
func (rb *ringBuffer) Write(buf []byte) (int, error) {
ringLen := cap(rb.buf)
for i, b := range buf {
rb.buf[(rb.off+i)%ringLen] = b
}
rb.off = (rb.off + len(buf)) % ringLen
rb.l = rb.l + len(buf)
if rb.l > ringLen {
rb.l = ringLen
}
return len(buf), nil
}
var NameToCmd = map[string]string{

41
misc/buildbot/bot_test.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"reflect"
"testing"
)
func TestRingBuffer(t *testing.T) {
rb := newRingBuffer(4)
data := []struct {
in, want string
}{
{in: "a", want: "a"},
{in: "b", want: "ab"},
{in: "c", want: "abc"},
{in: "d", want: "abcd"},
{in: "e", want: "bcde"},
{in: "f", want: "cdef"},
// Multibyte writes that wrap the ring buffer.
{in: "ghi", want: "fghi"},
{in: "jkl", want: "ijkl"},
{in: "mno", want: "lmno"},
// Write larger than ring buffer.
{in: "pqrstuv", want: "stuv"},
}
for i, d := range data {
in := []byte(d.in)
n, err := rb.Write(in)
if err != nil {
t.Error(err)
}
if n != len(in) {
t.Error(i, "Wrote", n, "bytes, want", len(in))
}
got := rb.Bytes()
want := []byte(d.want)
if !reflect.DeepEqual(want, got) {
t.Errorf("%d Got %q want %q", i, got, want)
}
}
}