From dda4d0d2b90765bd52d2930ba4eccd74aa00eff2 Mon Sep 17 00:00:00 2001 From: Jackson Owens Date: Tue, 17 Oct 2023 11:25:44 -0400 Subject: [PATCH] vfs/errorfs: add OpFileReadAt predicate 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. --- vfs/errorfs/dsl.go | 30 ++++++++++++++++++++++++++++++ vfs/errorfs/errorfs.go | 19 +++++++++++++++---- vfs/errorfs/testdata/errorfs | 9 +++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/vfs/errorfs/dsl.go b/vfs/errorfs/dsl.go index fbe8dc6ae9..14eb51d044 100644 --- a/vfs/errorfs/dsl.go +++ b/vfs/errorfs/dsl.go @@ -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 } @@ -144,6 +158,9 @@ func OnIndex(index int32) *InjectIndex { // - (Or [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 ) 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. @@ -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) } @@ -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} +} diff --git a/vfs/errorfs/errorfs.go b/vfs/errorfs/errorfs.go index 327052e016..9e002555e6 100644 --- a/vfs/errorfs/errorfs.go +++ b/vfs/errorfs/errorfs.go @@ -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. @@ -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) @@ -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) { diff --git a/vfs/errorfs/testdata/errorfs b/vfs/errorfs/testdata/errorfs index fa3cdea6fa..5292527bfb 100644 --- a/vfs/errorfs/testdata/errorfs +++ b/vfs/errorfs/testdata/errorfs @@ -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))