blob: 5086f285e08a49683c25e89e63a0d5d9f31324ce [file] [log] [blame]
// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package report
import (
"bytes"
"regexp"
)
type gvisor struct {
ignores []*regexp.Regexp
}
func ctorGvisor(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter, []string, error) {
ctx := &gvisor{
ignores: ignores,
}
suppressions := []string{
"fatal error: runtime: out of memory",
"fatal error: runtime: cannot allocate memory",
"panic: ptrace sysemu failed: no such process", // OOM kill
"panic: ptrace set fpregs failed: no such process", // OOM kill
"panic: ptrace set regs failed: no such process", // OOM kill
"panic: failed to start executor binary",
"panic: executor failed: pthread_create failed",
"ERROR: ThreadSanitizer", // Go race failing due to OOM.
"FATAL: ThreadSanitizer",
}
return ctx, suppressions, nil
}
func (ctx *gvisor) ContainsCrash(output []byte) bool {
return containsCrash(output, gvisorOopses, ctx.ignores)
}
func (ctx *gvisor) Parse(output []byte) *Report {
rep := simpleLineParser(output, gvisorOopses, nil, ctx.ignores)
if rep == nil {
return nil
}
rep.Title = replaceTable(gvisorTitleReplacement, rep.Title)
rep.Report = ctx.shortenReport(rep.Report)
return rep
}
func (ctx *gvisor) shortenReport(report []byte) []byte {
// gvisor panics include stacks of all goroutines.
// This output is too lengthy for report and not very useful.
// So we always take 5 lines from report and then cut it at the next empty line.
// The intention is to capture panic header and traceback of the first goroutine.
pos := 0
for i := 0; i < 5; i++ {
pos1 := bytes.IndexByte(report[pos:], '\n')
if pos1 == -1 {
return report
}
pos += pos1 + 1
}
end := bytes.Index(report[pos:], []byte{'\n', '\n'})
if end == -1 {
return report
}
if bytes.Contains(report, []byte("WARNING: DATA RACE")) {
// For data races extract both stacks.
end2 := bytes.Index(report[pos+end+2:], []byte{'\n', '\n'})
if end2 != -1 {
end += end2 + 2
}
}
return report[:pos+end+1]
}
func (ctx *gvisor) Symbolize(rep *Report) error {
return nil
}
var gvisorTitleReplacement = []replacement{
{
regexp.MustCompile(`container ".*"`),
"container NAME",
},
}
var gvisorOopses = []*oops{
{
[]byte("panic:"),
[]oopsFormat{
{
title: compile("panic:(.*)"),
fmt: "panic:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("Panic:"),
[]oopsFormat{
{
title: compile("Panic:(.*)"),
fmt: "Panic:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("fatal error:"),
[]oopsFormat{
{
title: compile("fatal error:(.*)"),
fmt: "fatal error:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("runtime error:"),
[]oopsFormat{
{
title: compile("runtime error:(.*)"),
fmt: "runtime error:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("SIGSEGV:"),
[]oopsFormat{
{
title: compile("SIGSEGV:(.*)"),
fmt: "SIGSEGV:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("SIGBUS:"),
[]oopsFormat{
{
title: compile("SIGBUS:(.*)"),
fmt: "SIGBUS:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("FATAL ERROR:"),
[]oopsFormat{
{
title: compile("FATAL ERROR:(.*)"),
fmt: "FATAL ERROR:%[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("WARNING: DATA RACE"),
[]oopsFormat{
{
title: compile("WARNING: DATA RACE"),
report: compile("WARNING: DATA RACE\n(?:.*\n)*? (?:[a-zA-Z0-9./-_]+/)([a-zA-Z0-9.()*_]+)\\(\\)\n"),
fmt: "DATA RACE in %[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
{
[]byte("Invalid request partialResult"),
[]oopsFormat{
{
title: compile("Invalid request partialResult"),
report: compile("Invalid request partialResult .* for (.*) operation"),
fmt: "Invalid request partialResult in %[1]v",
noStackTrace: true,
},
},
[]*regexp.Regexp{},
},
}