aboutsummaryrefslogtreecommitdiffstats
path: root/prog
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-12-29 15:00:57 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-12-29 15:00:57 +0100
commite6529b30ec934f285d57dc16dd8acbbab074f102 (patch)
tree9bf9673e44997f24e702833904294f3116d4f209 /prog
parentd40104b8a35f01d31cad1f11e312e76e034ffc4a (diff)
sys: add union type
Diffstat (limited to 'prog')
-rw-r--r--prog/analysis.go17
-rw-r--r--prog/clone.go2
-rw-r--r--prog/encoding.go26
-rw-r--r--prog/encodingexec.go9
-rw-r--r--prog/mutation.go10
-rw-r--r--prog/prio.go10
-rw-r--r--prog/prog.go13
-rw-r--r--prog/rand.go4
-rw-r--r--prog/validation.go20
9 files changed, 92 insertions, 19 deletions
diff --git a/prog/analysis.go b/prog/analysis.go
index 218ab7385..dc819664e 100644
--- a/prog/analysis.go
+++ b/prog/analysis.go
@@ -129,6 +129,9 @@ func foreachArgArray(args *[]*Arg, ret *Arg, f func(arg, base *Arg, parent *[]*A
if arg.Kind == ArgPointer && arg.Res != nil {
rec(arg.Res, arg, parent)
}
+ if arg.Kind == ArgUnion {
+ rec(arg.Option, base, parent)
+ }
}
for _, arg := range *args {
rec(arg, nil, args)
@@ -143,15 +146,14 @@ func foreachArg(c *Call, f func(arg, base *Arg, parent *[]*Arg)) {
}
func referencedArgs(args []*Arg, ret *Arg) (res []*Arg) {
- f := func(arg, _ *Arg, _ *[]*Arg) {
+ foreachArgArray(&args, ret, func(arg, _ *Arg, _ *[]*Arg) {
for arg1 := range arg.Uses {
if arg1.Kind != ArgResult {
panic("use references not ArgResult")
}
res = append(res, arg1)
}
- }
- foreachArgArray(&args, ret, f)
+ })
return
}
@@ -170,10 +172,6 @@ func assignTypeAndDir(c *Call) error {
case ArgPointer:
arg.Dir = DirIn
switch typ1 := typ.(type) {
- case sys.FilenameType:
- if err := rec(arg.Res, typ, dir); err != nil {
- return err
- }
case sys.PtrType:
if arg.Res != nil {
if err := rec(arg.Res, typ1.Type, ArgDir(typ1.Dir)); err != nil {
@@ -200,6 +198,11 @@ func assignTypeAndDir(c *Call) error {
}
}
}
+ case ArgUnion:
+ arg.Dir = dir
+ if err := rec(arg.Option, arg.OptionType, dir); err != nil {
+ return err
+ }
default:
arg.Dir = dir
}
diff --git a/prog/clone.go b/prog/clone.go
index dd8e6e4db..14f9db759 100644
--- a/prog/clone.go
+++ b/prog/clone.go
@@ -31,6 +31,8 @@ func (arg *Arg) clone(c *Call, newargs map[*Arg]*Arg) *Arg {
if arg.Res != nil {
arg1.Res = arg.Res.clone(c, newargs)
}
+ case ArgUnion:
+ arg1.Option = arg.Option.clone(c, newargs)
case ArgResult:
r := newargs[arg.Res]
arg1.Res = r
diff --git a/prog/encoding.go b/prog/encoding.go
index 47addb5ef..b9bd1ce16 100644
--- a/prog/encoding.go
+++ b/prog/encoding.go
@@ -109,6 +109,9 @@ func (a *Arg) serialize(buf io.Writer, vars map[*Arg]int, varSeq *int) {
a1.serialize(buf, vars, varSeq)
}
buf.Write([]byte{delims[1]})
+ case ArgUnion:
+ fmt.Fprintf(buf, "@%v=", a.OptionType.Name())
+ a.Option.serialize(buf, vars, varSeq)
default:
panic("unknown arg kind")
}
@@ -306,6 +309,29 @@ func parseArg(typ sys.Type, p *parser, vars map[string]*Arg) (*Arg, error) {
}
p.Parse(']')
arg = groupArg(inner)
+ case '@':
+ t1, ok := typ.(sys.UnionType)
+ if !ok {
+ return nil, fmt.Errorf("'@' arg is not a union: %#v", typ)
+ }
+ p.Parse('@')
+ name := p.Ident()
+ p.Parse('=')
+ var optType sys.Type
+ for _, t2 := range t1.Options {
+ if name == t2.Name() {
+ optType = t2
+ break
+ }
+ }
+ if optType == nil {
+ return nil, fmt.Errorf("union arg %v has unknown option: %v", typ.Name(), name)
+ }
+ opt, err := parseArg(optType, p, vars)
+ if err != nil {
+ return nil, err
+ }
+ arg = unionArg(opt, optType)
case 'n':
p.Parse('n')
p.Parse('i')
diff --git a/prog/encodingexec.go b/prog/encodingexec.go
index 3b68f6c98..2424a1caa 100644
--- a/prog/encodingexec.go
+++ b/prog/encodingexec.go
@@ -59,6 +59,10 @@ func (p *Prog) SerializeForExec() []byte {
}
return
}
+ if arg1.Kind == ArgUnion {
+ rec(arg1.Option)
+ return
+ }
if sys.IsPad(arg1.Type) {
return
}
@@ -172,11 +176,6 @@ func (w *execContext) writeArg(arg *Arg) {
}
w.write(v)
}
- case ArgGroup:
- // Squash groups.
- for _, arg1 := range arg.Inner {
- w.writeArg(arg1)
- }
default:
panic("unknown arg type")
}
diff --git a/prog/mutation.go b/prog/mutation.go
index 09cf90135..4a28a9997 100644
--- a/prog/mutation.go
+++ b/prog/mutation.go
@@ -154,6 +154,8 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) {
replaceArg(p, arg.Inner[i], f, calls1)
calls1 = nil
}
+ case sys.UnionType:
+ //!!! implement me
case sys.LenType:
panic("bad arg returned by mutationArgs: LenType")
case sys.ConstType, sys.StrConstType:
@@ -362,13 +364,11 @@ func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) {
foreachArg(c, func(arg, base *Arg, parent *[]*Arg) {
switch arg.Type.(type) {
case sys.StructType:
- switch arg.Type.Name() {
- default:
+ if isSpecialStruct(arg.Type) == nil {
// For structs only individual fields are updated.
return
- case "timespec", "timeval":
- // These special structs are mutated as a whole.
}
+ // These special structs are mutated as a whole.
case sys.LenType:
// Size is updated when the size-of arg change.
return
@@ -380,7 +380,7 @@ func mutationArgs(c *Call) (args, bases []*Arg, parents []*[]*Arg) {
return
}
if base != nil {
- if _, ok := base.Type.(sys.StructType); ok && (base.Type.Name() == "timespec" || base.Type.Name() == "timeval") {
+ if _, ok := base.Type.(sys.StructType); ok && isSpecialStruct(base.Type) != nil {
// These special structs are mutated as a whole.
return
}
diff --git a/prog/prio.go b/prog/prio.go
index 2e6e46da2..d6621cb2c 100644
--- a/prog/prio.go
+++ b/prog/prio.go
@@ -80,6 +80,12 @@ func calcStaticPriorities() [][]float32 {
if _, ok := a.Type.(sys.StructType); ok {
noteUsage(1.0, "ptrto-%v", a.Type.Name())
}
+ if _, ok := a.Type.(sys.UnionType); ok {
+ noteUsage(1.0, "ptrto-%v", a.Type.Name())
+ }
+ if arr, ok := a.Type.(sys.ArrayType); ok {
+ noteUsage(1.0, "ptrto-%v", arr.Type.Name())
+ }
case sys.BufferType:
switch a.Kind {
case sys.BufferBlob, sys.BufferFilesystem, sys.BufferAlgType, sys.BufferAlgName:
@@ -207,6 +213,10 @@ func foreachArgType(meta *sys.Call, f func(sys.Type, ArgDir)) {
for _, f := range a.Fields {
rec(f, d)
}
+ case sys.UnionType:
+ for _, opt := range a.Options {
+ rec(opt, d)
+ }
case sys.ResourceType, sys.FileoffType, sys.BufferType,
sys.VmaType, sys.LenType, sys.FlagsType, sys.ConstType,
sys.StrConstType, sys.IntType, sys.FilenameType:
diff --git a/prog/prog.go b/prog/prog.go
index e4c1c72ca..e9e3f3d71 100644
--- a/prog/prog.go
+++ b/prog/prog.go
@@ -31,6 +31,10 @@ type Arg struct {
Uses map[*Arg]bool // this arg is used by those ArgResult args
OpDiv uintptr // divide result for ArgResult (executed before OpAdd)
OpAdd uintptr // add to result for ArgResult
+
+ // ArgUnion/UnionType
+ Option *Arg
+ OptionType sys.Type
}
type ArgKind int
@@ -41,7 +45,8 @@ const (
ArgPointer // even if these are always constant (for reproducibility), we use a separate type because they are represented in an abstract (base+page+offset) form
ArgPageSize // same as ArgPointer but base is not added, so it represents "lengths" in pages
ArgData
- ArgGroup // logical group of args (struct or array)
+ ArgGroup // logical group of args (struct or array)
+ ArgUnion
ArgReturn // fake value denoting syscall return value
)
@@ -68,6 +73,8 @@ func (a *Arg) Size(typ sys.Type) uintptr {
size += a.Inner[i].Size(f)
}
return size
+ case sys.UnionType:
+ return a.Option.Size(a.OptionType)
case sys.ArrayType:
var size uintptr
for _, in := range a.Inner {
@@ -111,6 +118,10 @@ func groupArg(inner []*Arg) *Arg {
return &Arg{Kind: ArgGroup, Inner: inner}
}
+func unionArg(opt *Arg, typ sys.Type) *Arg {
+ return &Arg{Kind: ArgUnion, Option: opt, OptionType: typ}
+}
+
func returnArg() *Arg {
return &Arg{Kind: ArgReturn, Dir: DirOut}
}
diff --git a/prog/rand.go b/prog/rand.go
index accb6db23..f6d12a430 100644
--- a/prog/rand.go
+++ b/prog/rand.go
@@ -732,6 +732,10 @@ func (r *randGen) generateArg(s *state, typ sys.Type, dir ArgDir, sizes map[stri
}
args, calls := r.generateArgs(s, a.Fields, dir)
return groupArg(args), nil, calls
+ case sys.UnionType:
+ optType := a.Options[r.Intn(len(a.Options))]
+ opt, size, calls := r.generateArg(s, optType, dir, sizes)
+ return unionArg(opt, optType), size, calls
case sys.PtrType:
inner, size, calls := r.generateArg(s, a.Type, ArgDir(a.Dir), sizes)
if ArgDir(a.Dir) == DirOut && inner == nil {
diff --git a/prog/validation.go b/prog/validation.go
index cc6d1ae61..4cb08c7fc 100644
--- a/prog/validation.go
+++ b/prog/validation.go
@@ -123,7 +123,7 @@ func (c *Call) validate(ctx *validCtx) error {
switch typ1 := typ.(type) {
case sys.StructType:
if len(arg.Inner) != len(typ1.Fields) {
- return fmt.Errorf("syscall %v: struct arg '%v' has wrong number of fields, want %v, got %v", c.Meta.Name, typ.Name(), len(typ1.Fields), len(arg.Inner))
+ return fmt.Errorf("syscall %v: struct arg '%v' has wrong number of fields: want %v, got %v", c.Meta.Name, typ.Name(), len(typ1.Fields), len(arg.Inner))
}
for i, arg1 := range arg.Inner {
if err := checkArg(arg1, typ1.Fields[i]); err != nil {
@@ -139,6 +139,24 @@ func (c *Call) validate(ctx *validCtx) error {
default:
return fmt.Errorf("syscall %v: group arg '%v' has bad underlying type %+v", c.Meta.Name, typ.Name(), typ)
}
+ case ArgUnion:
+ typ1, ok := typ.(sys.UnionType)
+ if !ok {
+ return fmt.Errorf("syscall %v: union arg '%v' has bad type", c.Meta.Name, typ.Name())
+ }
+ found := false
+ for _, typ2 := range typ1.Options {
+ if arg.OptionType.Name() == typ2.Name() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return fmt.Errorf("syscall %v: union arg '%v' has bad option", c.Meta.Name, typ.Name())
+ }
+ if err := checkArg(arg.Option, arg.OptionType); err != nil {
+ return err
+ }
case ArgReturn:
default:
return fmt.Errorf("syscall %v: unknown arg '%v' kind", c.Meta.Name, typ.Name())