Show fuse ops as spans in traced contexts. Plumb through in tests.
commit
cecf227799
|
@ -21,6 +21,8 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/jacobsa/bazilfuse"
|
"github.com/jacobsa/bazilfuse"
|
||||||
"github.com/jacobsa/fuse/fuseops"
|
"github.com/jacobsa/fuse/fuseops"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +33,9 @@ type Connection struct {
|
||||||
wrapped *bazilfuse.Conn
|
wrapped *bazilfuse.Conn
|
||||||
opsInFlight sync.WaitGroup
|
opsInFlight sync.WaitGroup
|
||||||
|
|
||||||
|
// The context from which all op contexts inherit.
|
||||||
|
parentCtx context.Context
|
||||||
|
|
||||||
// For logging purposes only.
|
// For logging purposes only.
|
||||||
nextOpID uint32
|
nextOpID uint32
|
||||||
}
|
}
|
||||||
|
@ -38,11 +43,13 @@ type Connection struct {
|
||||||
// Responsibility for closing the wrapped connection is transferred to the
|
// Responsibility for closing the wrapped connection is transferred to the
|
||||||
// result. You must call c.close() eventually.
|
// result. You must call c.close() eventually.
|
||||||
func newConnection(
|
func newConnection(
|
||||||
|
parentCtx context.Context,
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
wrapped *bazilfuse.Conn) (c *Connection, err error) {
|
wrapped *bazilfuse.Conn) (c *Connection, err error) {
|
||||||
c = &Connection{
|
c = &Connection{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
wrapped: wrapped,
|
wrapped: wrapped,
|
||||||
|
parentCtx: parentCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -115,7 +122,8 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
|
||||||
c.log(opID, calldepth+1, format, v...)
|
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)
|
c.log(opID, 1, "-> ENOSYS: %v", bfReq)
|
||||||
bfReq.RespondError(ENOSYS)
|
bfReq.RespondError(ENOSYS)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -16,31 +16,56 @@ package fuseops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/jacobsa/bazilfuse"
|
"github.com/jacobsa/bazilfuse"
|
||||||
|
"github.com/jacobsa/reqtrace"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A helper for embedding common behavior.
|
// A helper for embedding common behavior.
|
||||||
type commonOp struct {
|
type commonOp struct {
|
||||||
opType string
|
opType string
|
||||||
ctx context.Context
|
|
||||||
r bazilfuse.Request
|
r bazilfuse.Request
|
||||||
log func(int, string, ...interface{})
|
log func(int, string, ...interface{})
|
||||||
opsInFlight *sync.WaitGroup
|
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(
|
func (o *commonOp) init(
|
||||||
opType string,
|
ctx context.Context,
|
||||||
|
opType reflect.Type,
|
||||||
r bazilfuse.Request,
|
r bazilfuse.Request,
|
||||||
log func(int, string, ...interface{}),
|
log func(int, string, ...interface{}),
|
||||||
opsInFlight *sync.WaitGroup) {
|
opsInFlight *sync.WaitGroup) {
|
||||||
o.opType = opType
|
// Initialize basic fields.
|
||||||
o.ctx = context.Background()
|
o.opType = describeOpType(opType)
|
||||||
o.r = r
|
o.r = r
|
||||||
o.log = log
|
o.log = log
|
||||||
o.opsInFlight = opsInFlight
|
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 {
|
func (o *commonOp) Header() OpHeader {
|
||||||
|
@ -65,6 +90,8 @@ func (o *commonOp) respondErr(err error) {
|
||||||
panic("Expect non-nil here.")
|
panic("Expect non-nil here.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.report(err)
|
||||||
|
|
||||||
o.Logf(
|
o.Logf(
|
||||||
"-> (%s) error: %v",
|
"-> (%s) error: %v",
|
||||||
o.opType,
|
o.opType,
|
||||||
|
@ -78,6 +105,9 @@ func (o *commonOp) respondErr(err error) {
|
||||||
//
|
//
|
||||||
// Special case: nil means o.r.Respond accepts no parameters.
|
// Special case: nil means o.r.Respond accepts no parameters.
|
||||||
func (o *commonOp) respond(resp interface{}) {
|
func (o *commonOp) respond(resp interface{}) {
|
||||||
|
// We were successful.
|
||||||
|
o.report(nil)
|
||||||
|
|
||||||
// Find the Respond method.
|
// Find the Respond method.
|
||||||
v := reflect.ValueOf(o.r)
|
v := reflect.ValueOf(o.r)
|
||||||
respond := v.MethodByName("Respond")
|
respond := v.MethodByName("Respond")
|
||||||
|
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/jacobsa/bazilfuse"
|
"github.com/jacobsa/bazilfuse"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@ import (
|
||||||
// This function is an implementation detail of the fuse package, and must not
|
// This function is an implementation detail of the fuse package, and must not
|
||||||
// be called by anyone else.
|
// be called by anyone else.
|
||||||
func Convert(
|
func Convert(
|
||||||
|
parentCtx context.Context,
|
||||||
r bazilfuse.Request,
|
r bazilfuse.Request,
|
||||||
logForOp func(int, string, ...interface{}),
|
logForOp func(int, string, ...interface{}),
|
||||||
opsInFlight *sync.WaitGroup) (o Op) {
|
opsInFlight *sync.WaitGroup) (o Op) {
|
||||||
|
@ -213,7 +216,7 @@ func Convert(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
co.init(reflect.TypeOf(o).String(), r, logForOp, opsInFlight)
|
co.init(parentCtx, reflect.TypeOf(o), r, logForOp, opsInFlight)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,10 @@ func (mfs *MountedFileSystem) Join(ctx context.Context) error {
|
||||||
|
|
||||||
// Optional configuration accepted by Mount.
|
// Optional configuration accepted by Mount.
|
||||||
type MountConfig struct {
|
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.
|
// OS X only.
|
||||||
//
|
//
|
||||||
// Normally on OS X we mount with the novncache option
|
// Normally on OS X we mount with the novncache option
|
||||||
|
@ -127,8 +131,14 @@ func Mount(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Choose a parent context for ops.
|
||||||
|
opContext := config.OpContext
|
||||||
|
if opContext == nil {
|
||||||
|
opContext = context.Background()
|
||||||
|
}
|
||||||
|
|
||||||
// Create our own Connection object wrapping it.
|
// Create our own Connection object wrapping it.
|
||||||
connection, err := newConnection(logger, bfConn)
|
connection, err := newConnection(opContext, logger, bfConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bfConn.Close()
|
bfConn.Close()
|
||||||
err = fmt.Errorf("newConnection: %v", err)
|
err = fmt.Errorf("newConnection: %v", err)
|
||||||
|
|
|
@ -59,7 +59,7 @@ type SampleTest struct {
|
||||||
//
|
//
|
||||||
// REQUIRES: t.Server has been set.
|
// REQUIRES: t.Server has been set.
|
||||||
func (t *SampleTest) SetUp(ti *ogletest.TestInfo) {
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -67,10 +67,17 @@ func (t *SampleTest) SetUp(ti *ogletest.TestInfo) {
|
||||||
|
|
||||||
// Like SetUp, but doens't panic.
|
// Like SetUp, but doens't panic.
|
||||||
func (t *SampleTest) initialize(
|
func (t *SampleTest) initialize(
|
||||||
|
ctx context.Context,
|
||||||
server fuse.Server,
|
server fuse.Server,
|
||||||
config *fuse.MountConfig) (err error) {
|
config *fuse.MountConfig) (err error) {
|
||||||
// Initialize the context.
|
// Initialize the context used by the test.
|
||||||
t.Ctx = context.Background()
|
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.
|
// Initialize the clock.
|
||||||
t.Clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))
|
t.Clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))
|
||||||
|
|
|
@ -68,7 +68,7 @@ type SubprocessTest struct {
|
||||||
// Mount the file system and initialize the other exported fields of the
|
// Mount the file system and initialize the other exported fields of the
|
||||||
// struct. Panics on error.
|
// struct. Panics on error.
|
||||||
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
|
func (t *SubprocessTest) SetUp(ti *ogletest.TestInfo) {
|
||||||
err := t.initialize()
|
err := t.initialize(ti.Ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -184,9 +184,9 @@ func waitForReady(readyReader *os.File, c chan<- struct{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like SetUp, but doens't panic.
|
// Like SetUp, but doens't panic.
|
||||||
func (t *SubprocessTest) initialize() (err error) {
|
func (t *SubprocessTest) initialize(ctx context.Context) (err error) {
|
||||||
// Initialize the context.
|
// Initialize the context.
|
||||||
t.Ctx = context.Background()
|
t.Ctx = ctx
|
||||||
|
|
||||||
// Set up a temporary directory.
|
// Set up a temporary directory.
|
||||||
t.Dir, err = ioutil.TempDir("", "sample_test")
|
t.Dir, err = ioutil.TempDir("", "sample_test")
|
||||||
|
|
Loading…
Reference in New Issue