diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-08-01 16:28:46 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-08-02 16:57:31 +0200 |
| commit | 2e17e2c0adcbae89c1634e4fadc398494b6639fb (patch) | |
| tree | ff4cdf5d55a6ffe286cf240cdbf8df918c4a9042 | |
| parent | c67a9331a4f7c25df943ff821c9c6ed8013df869 (diff) | |
vm: refactor MonitorExecution
Too complex. Split into several functions.
Update #538
| -rw-r--r-- | vm/vm.go | 173 |
1 files changed, 95 insertions, 78 deletions
@@ -123,68 +123,14 @@ func (inst *Instance) Close() { // If canExit is false and the program exits, it is treated as an error. // Returns a non-symbolized crash report, or nil if no error happens. func (inst *Instance) MonitorExecution(outc <-chan []byte, errc <-chan error, - reporter report.Reporter, canExit bool) ( - rep *report.Report) { - var output []byte - waitForOutput := func() { - timer := time.NewTimer(10 * time.Second).C - for { - select { - case out, ok := <-outc: - if !ok { - return - } - output = append(output, out...) - case <-timer: - return - } - } + reporter report.Reporter, canExit bool) (rep *report.Report) { + mon := &monitor{ + inst: inst, + outc: outc, + errc: errc, + reporter: reporter, + canExit: canExit, } - - matchPos := 0 - const ( - beforeContext = 1024 << 10 - afterContext = 128 << 10 - ) - extractError := func(defaultError string) *report.Report { - // Give it some time to finish writing the error message. - inst.Diagnose() - waitForOutput() - if bytes.Contains(output, []byte("SYZ-FUZZER: PREEMPTED")) { - return nil - } - if !reporter.ContainsCrash(output[matchPos:]) { - if defaultError == "" { - if canExit { - return nil - } - defaultError = "lost connection to test machine" - } - rep := &report.Report{ - Title: defaultError, - Output: output, - Suppressed: report.IsSuppressed(reporter, output), - } - return rep - } - rep := reporter.Parse(output[matchPos:]) - if rep == nil { - panic(fmt.Sprintf("reporter.ContainsCrash/Parse disagree:\n%s", output[matchPos:])) - } - start := matchPos + rep.StartPos - beforeContext - if start < 0 { - start = 0 - } - end := matchPos + rep.EndPos + afterContext - if end > len(output) { - end = len(output) - } - rep.Output = output[start:end] - rep.StartPos += matchPos - start - rep.EndPos += matchPos - start - return rep - } - lastExecuteTime := time.Now() ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() @@ -195,31 +141,31 @@ func (inst *Instance) MonitorExecution(outc <-chan []byte, errc <-chan error, case nil: // The program has exited without errors, // but wait for kernel output in case there is some delayed oops. - return extractError("") + return mon.extractError("") case ErrTimeout: return nil default: // Note: connection lost can race with a kernel oops message. // In such case we want to return the kernel oops. - return extractError("lost connection to test machine") + return mon.extractError("lost connection to test machine") } case out := <-outc: - lastPos := len(output) - output = append(output, out...) - if bytes.Contains(output[lastPos:], executingProgram1) || - bytes.Contains(output[lastPos:], executingProgram2) { + lastPos := len(mon.output) + mon.output = append(mon.output, out...) + if bytes.Contains(mon.output[lastPos:], executingProgram1) || + bytes.Contains(mon.output[lastPos:], executingProgram2) { lastExecuteTime = time.Now() } - if reporter.ContainsCrash(output[matchPos:]) { - return extractError("unknown error") + if reporter.ContainsCrash(mon.output[mon.matchPos:]) { + return mon.extractError("unknown error") } - if len(output) > 2*beforeContext { - copy(output, output[len(output)-beforeContext:]) - output = output[:beforeContext] + if len(mon.output) > 2*beforeContext { + copy(mon.output, mon.output[len(mon.output)-beforeContext:]) + mon.output = mon.output[:beforeContext] } - matchPos = len(output) - 512 - if matchPos < 0 { - matchPos = 0 + mon.matchPos = len(mon.output) - maxErrorLength + if mon.matchPos < 0 { + mon.matchPos = 0 } case <-ticker.C: // Detect both "not output whatsoever" and "kernel episodically prints @@ -237,12 +183,12 @@ func (inst *Instance) MonitorExecution(outc <-chan []byte, errc <-chan error, break } if inst.Diagnose() { - waitForOutput() + mon.waitForOutput() } rep := &report.Report{ Title: "no output from test machine", - Output: output, - Suppressed: report.IsSuppressed(reporter, output), + Output: mon.output, + Suppressed: report.IsSuppressed(mon.reporter, mon.output), } return rep case <-Shutdown: @@ -251,6 +197,77 @@ func (inst *Instance) MonitorExecution(outc <-chan []byte, errc <-chan error, } } +type monitor struct { + inst *Instance + outc <-chan []byte + errc <-chan error + reporter report.Reporter + canExit bool + output []byte + matchPos int +} + +func (mon *monitor) extractError(defaultError string) *report.Report { + // Give it some time to finish writing the error message. + mon.inst.Diagnose() + mon.waitForOutput() + if bytes.Contains(mon.output, []byte("SYZ-FUZZER: PREEMPTED")) { + return nil + } + if !mon.reporter.ContainsCrash(mon.output[mon.matchPos:]) { + if defaultError == "" { + if mon.canExit { + return nil + } + defaultError = "lost connection to test machine" + } + rep := &report.Report{ + Title: defaultError, + Output: mon.output, + Suppressed: report.IsSuppressed(mon.reporter, mon.output), + } + return rep + } + rep := mon.reporter.Parse(mon.output[mon.matchPos:]) + if rep == nil { + panic(fmt.Sprintf("reporter.ContainsCrash/Parse disagree:\n%s", mon.output[mon.matchPos:])) + } + start := mon.matchPos + rep.StartPos - beforeContext + if start < 0 { + start = 0 + } + end := mon.matchPos + rep.EndPos + afterContext + if end > len(mon.output) { + end = len(mon.output) + } + rep.Output = mon.output[start:end] + rep.StartPos += mon.matchPos - start + rep.EndPos += mon.matchPos - start + return rep +} + +func (mon *monitor) waitForOutput() { + timer := time.NewTimer(10 * time.Second) + for { + select { + case out, ok := <-mon.outc: + if !ok { + timer.Stop() + return + } + mon.output = append(mon.output, out...) + case <-timer.C: + return + } + } +} + +const ( + beforeContext = 1024 << 10 + afterContext = 128 << 10 + maxErrorLength = 512 +) + var ( executingProgram1 = []byte("executing program") // syz-fuzzer output executingProgram2 = []byte("executed programs:") // syz-execprog output |
