Show fuse ops as spans in traced contexts. Plumb through in tests.

geesefs-0-30-9
Aaron Jacobs 2015-05-01 11:25:48 +10:00
commit cecf227799
6 changed files with 73 additions and 15 deletions

View File

@ -21,6 +21,8 @@ import (
"runtime"
"sync"
"golang.org/x/net/context"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/fuse/fuseops"
)
@ -31,6 +33,9 @@ type Connection struct {
wrapped *bazilfuse.Conn
opsInFlight sync.WaitGroup
// The context from which all op contexts inherit.
parentCtx context.Context
// For logging purposes only.
nextOpID uint32
}
@ -38,11 +43,13 @@ type Connection struct {
// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
parentCtx context.Context,
logger *log.Logger,
wrapped *bazilfuse.Conn) (c *Connection, err error) {
c = &Connection{
logger: logger,
wrapped: wrapped,
logger: logger,
wrapped: wrapped,
parentCtx: parentCtx,
}
return
@ -115,7 +122,8 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
c.log(opID, calldepth+1, format, v...)
}
if op = fuseops.Convert(bfReq, logForOp, &c.opsInFlight); op == nil {
op = fuseops.Convert(c.parentCtx, bfReq, logForOp, &c.opsInFlight)
if op == nil {
c.log(opID, 1, "-> ENOSYS: %v", bfReq)
bfReq.RespondError(ENOSYS)
continue

View File

@ -16,31 +16,56 @@ package fuseops
import (
"reflect"
"strings"
"sync"
"github.com/jacobsa/bazilfuse"
"github.com/jacobsa/reqtrace"
"golang.org/x/net/context"
)
// A helper for embedding common behavior.
type commonOp struct {
opType string
ctx context.Context
r bazilfuse.Request
log func(int, string, ...interface{})
opsInFlight *sync.WaitGroup
ctx context.Context
report reqtrace.ReportFunc
}
func describeOpType(t reflect.Type) (desc string) {
name := t.String()
// The usual case: a string that looks like "*fuseops.GetInodeAttributesOp".
const prefix = "*fuseops."
const suffix = "Op"
if strings.HasPrefix(name, prefix) && strings.HasSuffix(name, suffix) {
desc = name[len(prefix) : len(name)-len(suffix)]
return
}
// Otherwise, it's not clear what to do.
desc = t.String()
return
}
func (o *commonOp) init(
opType string,
ctx context.Context,
opType reflect.Type,
r bazilfuse.Request,
log func(int, string, ...interface{}),
opsInFlight *sync.WaitGroup) {
o.opType = opType
o.ctx = context.Background()
// Initialize basic fields.
o.opType = describeOpType(opType)
o.r = r
o.log = log
o.opsInFlight = opsInFlight
// Set up a trace span for this op.
o.ctx, o.report = reqtrace.StartSpan(ctx, o.opType)
}
func (o *commonOp) Header() OpHeader {
@ -65,6 +90,8 @@ func (o *commonOp) respondErr(err error) {
panic("Expect non-nil here.")
}
o.report(err)
o.Logf(
"-> (%s) error: %v",
o.opType,
@ -78,6 +105,9 @@ func (o *commonOp) respondErr(err error) {
//
// Special case: nil means o.r.Respond accepts no parameters.
func (o *commonOp) respond(resp interface{}) {
// We were successful.
o.report(nil)
// Find the Respond method.
v := reflect.ValueOf(o.r)
respond := v.MethodByName("Respond")

View File

@ -19,6 +19,8 @@ import (
"sync"
"time"
"golang.org/x/net/context"
"github.com/jacobsa/bazilfuse"
)
@ -28,6 +30,7 @@ import (
// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
func Convert(
parentCtx context.Context,
r bazilfuse.Request,
logForOp func(int, string, ...interface{}),
opsInFlight *sync.WaitGroup) (o Op) {
@ -213,7 +216,7 @@ func Convert(
return
}
co.init(reflect.TypeOf(o).String(), r, logForOp, opsInFlight)
co.init(parentCtx, reflect.TypeOf(o), r, logForOp, opsInFlight)
return
}

View File

@ -61,6 +61,10 @@ func (mfs *MountedFileSystem) Join(ctx context.Context) error {
// Optional configuration accepted by Mount.
type MountConfig struct {
// The context from which every op read from the connetion by the sever
// should inherit. If nil, context.Background() will be used.
OpContext context.Context
// OS X only.
//
// Normally on OS X we mount with the novncache option
@ -127,8 +131,14 @@ func Mount(
return
}
// Choose a parent context for ops.
opContext := config.OpContext
if opContext == nil {
opContext = context.Background()
}
// Create our own Connection object wrapping it.
connection, err := newConnection(logger, bfConn)
connection, err := newConnection(opContext, logger, bfConn)
if err != nil {
bfConn.Close()
err = fmt.Errorf("newConnection: %v", err)

View File

@ -59,7 +59,7 @@ type SampleTest struct {
//
// REQUIRES: t.Server has been set.
func (t *SampleTest) SetUp(ti *ogletest.TestInfo) {
err := t.initialize(t.Server, &t.MountConfig)
err := t.initialize(ti.Ctx, t.Server, &t.MountConfig)
if err != nil {
panic(err)
}
@ -67,10 +67,17 @@ func (t *SampleTest) SetUp(ti *ogletest.TestInfo) {
// Like SetUp, but doens't panic.
func (t *SampleTest) initialize(
ctx context.Context,
server fuse.Server,
config *fuse.MountConfig) (err error) {
// Initialize the context.
t.Ctx = context.Background()
// Initialize the context used by the test.
t.Ctx = ctx
// Make the server share that context, if the test hasn't already set some
// other one.
if config.OpContext == nil {
config.OpContext = ctx
}
// Initialize the clock.
t.Clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))

View File

@ -68,7 +68,7 @@ type SubprocessTest struct {
// Mount the file system and initialize the other exported fields of the
// struct. Panics on error.
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
err := t.initialize()
err := t.initialize(ti.Ctx)
if err != nil {
panic(err)
}
@ -184,9 +184,9 @@ func waitForReady(readyReader *os.File, c chan<- struct{}) {
}
// Like SetUp, but doens't panic.
func (t *SubprocessTest) initialize() (err error) {
func (t *SubprocessTest) initialize(ctx context.Context) (err error) {
// Initialize the context.
t.Ctx = context.Background()
t.Ctx = ctx
// Set up a temporary directory.
t.Dir, err = ioutil.TempDir("", "sample_test")