prog: glue mmap's together during minimization

This commit is contained in:
Dmitry Vyukov 2015-12-23 13:47:45 +01:00
parent 6af1c1f308
commit 28571fdc32
2 changed files with 102 additions and 14 deletions

View File

@ -235,10 +235,71 @@ func (p *Prog) Mutate(rs rand.Source, ncalls int, ct *ChoiceTable) {
// whether it is equal to the orginal program or not. If it is equivalent then
// the simplification attempt is committed and the process continues.
func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool) (*Prog, int) {
if callIndex0 < 0 || callIndex0 >= len(p0.Calls) {
panic("bad call index")
name0 := ""
if callIndex0 != -1 {
if callIndex0 < 0 || callIndex0 >= len(p0.Calls) {
panic("bad call index")
}
name0 = p0.Calls[callIndex0].Meta.Name
}
name0 := p0.Calls[callIndex0].Meta.Name
// Try to glue all mmap's together.
s := analyze(nil, p0, nil)
hi := -1
for i := 0; i < maxPages; i++ {
if s.pages[i] {
hi = i
}
}
if hi != -1 {
p := p0.Clone()
callIndex := callIndex0
// Remove all mmaps.
for i := 0; i < len(p.Calls); i++ {
c := p.Calls[i]
if i != callIndex && c.Meta.Name == "mmap" {
copy(p.Calls[i:], p.Calls[i+1:])
p.Calls = p.Calls[:len(p.Calls)-1]
for _, arg := range referencedArgs(c.Args, c.Ret) {
arg1 := constArg(arg.Type.Default())
replaceArg(p, arg, arg1, nil)
}
foreachArg(c, func(arg, _ *Arg, _ *[]*Arg) {
if arg.Kind == ArgResult {
delete(arg.Res.Uses, arg)
}
})
if i < callIndex {
callIndex--
}
i--
}
}
// Prepend uber-mmap.
mmap := &Call{
Meta: sys.CallMap["mmap"],
Args: []*Arg{
pointerArg(0, 0, nil),
pageSizeArg(uintptr(hi)+1, 0),
constArg(PROT_READ | PROT_WRITE),
constArg(MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED),
constArg(sys.InvalidFD),
constArg(0),
},
}
assignTypeAndDir(mmap)
p.Calls = append([]*Call{mmap}, p.Calls...)
if callIndex != -1 {
callIndex++
}
if pred(p, callIndex) {
p0 = p
callIndex0 = callIndex
}
}
// Try to remove all calls except the last one one-by-one.
for i := len(p0.Calls) - 1; i >= 0; i-- {
if i == callIndex0 {
@ -248,9 +309,8 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool) (*Prog, int)
if i < callIndex {
callIndex--
}
c := p0.Calls[i]
p := p0.Clone()
c = p.Calls[i]
c := p.Calls[i]
copy(p.Calls[i:], p.Calls[i+1:])
p.Calls = p.Calls[:len(p.Calls)-1]
for _, arg := range referencedArgs(c.Args, c.Ret) {
@ -274,9 +334,11 @@ func Minimize(p0 *Prog, callIndex0 int, pred func(*Prog, int) bool) (*Prog, int)
// - remove offsets from addresses
// - replace file descriptors with -1
// etc
if callIndex0 < 0 || callIndex0 >= len(p0.Calls) || name0 != p0.Calls[callIndex0].Meta.Name {
panic(fmt.Sprintf("bad call index after minimizatoin: ncalls=%v index=%v call=%v/%v",
len(p0.Calls), callIndex0, name0, p0.Calls[callIndex0].Meta.Name))
if callIndex0 != -1 {
if callIndex0 < 0 || callIndex0 >= len(p0.Calls) || name0 != p0.Calls[callIndex0].Meta.Name {
panic(fmt.Sprintf("bad call index after minimizatoin: ncalls=%v index=%v call=%v/%v",
len(p0.Calls), callIndex0, name0, p0.Calls[callIndex0].Meta.Name))
}
}
return p0, callIndex0
}

View File

@ -150,10 +150,11 @@ nextTest:
func TestMinimize(t *testing.T) {
tests := []struct {
orig string
callIndex int
pred func(*Prog, int) bool
result string
orig string
callIndex int
pred func(*Prog, int) bool
result string
resultCallIndex int
}{
// Predicate always returns false, so must get the same program.
{
@ -173,6 +174,7 @@ func TestMinimize(t *testing.T) {
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
2,
},
// Remove a call.
{
@ -186,6 +188,7 @@ func TestMinimize(t *testing.T) {
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"pipe2(&(0x7f0000000000)={0x0, 0x0}, 0x0)\n",
1,
},
// Remove two dependent calls.
{
@ -204,6 +207,7 @@ func TestMinimize(t *testing.T) {
return false
},
"sched_yield()\n",
0,
},
// Remove a call and replace results.
{
@ -218,6 +222,7 @@ func TestMinimize(t *testing.T) {
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
2,
},
// Remove a call and replace results.
{
@ -225,13 +230,30 @@ func TestMinimize(t *testing.T) {
"r0=open(&(0x7f0000000000)=\"1155\", 0x0, 0x0)\n" +
"write(r0, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
3,
-1,
func(p *Prog, callIndex int) bool {
return p.String() == "mmap-write-sched_yield"
},
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"write(0xffffffffffffffff, &(0x7f0000000000)=\"1155\", 0x2)\n" +
"sched_yield()\n",
-1,
},
// Glue several mmaps together.
{
"sched_yield()\n" +
"mmap(&(0x7f0000000000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"mmap(&(0x7f0000001000)=nil, (0x1000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"getpid()\n" +
"mmap(&(0x7f0000005000)=nil, (0x2000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n",
3,
func(p *Prog, callIndex int) bool {
return p.String() == "mmap-sched_yield-getpid"
},
"mmap(&(0x7f0000000000)=nil, (0x7000), 0x3, 0x32, 0xffffffffffffffff, 0x0)\n" +
"sched_yield()\n" +
"getpid()\n",
2,
},
}
for ti, test := range tests {
@ -239,12 +261,16 @@ func TestMinimize(t *testing.T) {
if err != nil {
t.Fatalf("failed to deserialize original program: %v", err)
}
p1, _ := Minimize(p, test.callIndex, test.pred)
p1, ci := Minimize(p, test.callIndex, test.pred)
res := p1.Serialize()
if string(res) != test.result {
t.Fatalf("minimization produced wrong result #%v\norig:\n%v\nexpect:\n%v\ngot:\n%v\n",
ti, test.orig, test.result, string(res))
}
if ci != test.resultCallIndex {
t.Fatalf("minimization broke call index #%v: got %v, want %v",
ti, ci, test.resultCallIndex)
}
}
}