Skip to content

Commit

Permalink
vfs/errorfs: add OpFileReadAt predicate
Browse files Browse the repository at this point in the history
Add a predicate that evaluates to true only for (vfs.File).ReadAt operations at
the provided offset. This allows datadriven tests to inject errors into
specific block loads if they know the layout of the sstable.
  • Loading branch information
jbowens committed Oct 20, 2023
1 parent 593a72b commit dda4d0d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 4 deletions.
30 changes: 30 additions & 0 deletions vfs/errorfs/dsl.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ var (
Writes Predicate = opKindPred{kind: OpIsWrite}
)

type opFileReadAt struct {
// offset configures the predicate to evaluate to true only if the
// operation's offset exactly matches offset.
offset int64
}

func (o *opFileReadAt) String() string {
return fmt.Sprintf("(FileReadAt %d)", o.offset)
}

func (o *opFileReadAt) evaluate(op Op) bool {
return op.Kind == OpFileReadAt && o.offset == op.Offset
}

type opKindPred struct {
kind OpReadWrite
}
Expand Down Expand Up @@ -144,6 +158,9 @@ func OnIndex(index int32) *InjectIndex {
// - (Or <PREDICATE> [PREDICATE]...) is a predicate that evaluates to true iff
// at least one of the provided predicates evaluates to true. Or short
// circuits on the first predicate to evaluate to true.
// - Operation-specific:
// (OpFileReadAt <INTEGER>) is a predicate that evaluates to true iff
// an operation is a file ReadAt call with an offset that's exactly equal.
//
// Example: (ErrInjected (And (PathMatch "*.sst") (OnIndex 5))) is a rule set
// that will inject an error on the 5-th I/O operation involving an sstable.
Expand Down Expand Up @@ -248,6 +265,9 @@ func init() {
"Or": func(s *scanner.Scanner) Predicate {
return Or(parseVariadicPredicate(s)...)
},
"OpFileReadAt": func(s *scanner.Scanner) Predicate {
return parseFileReadAtOp(s)
},
}
AddError(ErrInjected)
}
Expand Down Expand Up @@ -337,3 +357,13 @@ func mustUnquote(lit string) string {
}
return s
}

func parseFileReadAtOp(s *scanner.Scanner) *opFileReadAt {
lit := consumeTok(s, token.INT)
off, err := strconv.ParseInt(lit, 10, 64)
if err != nil {
panic(err)
}
consumeTok(s, token.RPAREN)
return &opFileReadAt{offset: off}
}
19 changes: 15 additions & 4 deletions vfs/errorfs/errorfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type Op struct {
Kind OpKind
// Path is the path of the file of the file being operated on.
Path string
// Offset is the offset of an operation. It's set for OpFileReadAt and
// OpFileWriteAt operations.
Offset int64
}

// OpKind is an enum describing the type of operation.
Expand Down Expand Up @@ -423,7 +426,11 @@ func (f *errorFile) Read(p []byte) (int, error) {
}

func (f *errorFile) ReadAt(p []byte, off int64) (int, error) {
if err := f.inj.MaybeError(Op{Kind: OpFileReadAt, Path: f.path}); err != nil {
if err := f.inj.MaybeError(Op{
Kind: OpFileReadAt,
Path: f.path,
Offset: off,
}); err != nil {
return 0, err
}
return f.file.ReadAt(p, off)
Expand All @@ -436,11 +443,15 @@ func (f *errorFile) Write(p []byte) (int, error) {
return f.file.Write(p)
}

func (f *errorFile) WriteAt(p []byte, ofs int64) (int, error) {
if err := f.inj.MaybeError(Op{Kind: OpFileWriteAt, Path: f.path}); err != nil {
func (f *errorFile) WriteAt(p []byte, off int64) (int, error) {
if err := f.inj.MaybeError(Op{
Kind: OpFileWriteAt,
Path: f.path,
Offset: off,
}); err != nil {
return 0, err
}
return f.file.WriteAt(p, ofs)
return f.file.WriteAt(p, off)
}

func (f *errorFile) Stat() (os.FileInfo, error) {
Expand Down
9 changes: 9 additions & 0 deletions vfs/errorfs/testdata/errorfs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,12 @@ parsing err: errorfs: unknown error "And"
parsing err: errorfs: unknown error "Or"
parsing err: errorfs: unexpected token IDENT ("foo") at char 23; expected INT
parsing err: strconv.ParseInt: parsing "9223372036854775807": value out of range

parse-dsl
(ErrInjected (OpFileReadAt _))
(ErrInjected (OpFileReadAt foo))
(ErrInjected (OpFileReadAt 1052363))
----
parsing err: errorfs: unexpected token IDENT ("_") at char 28; expected INT
parsing err: errorfs: unexpected token IDENT ("foo") at char 28; expected INT
(ErrInjected (FileReadAt 1052363))

0 comments on commit dda4d0d

Please sign in to comment.