Skip to content

Commit

Permalink
pkg/report: improve akaros reporter and implement symbolization
Browse files Browse the repository at this point in the history
  • Loading branch information
dvyukov committed Jul 16, 2018
1 parent df5ae5f commit bad4246
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 40 deletions.
123 changes: 112 additions & 11 deletions pkg/report/akaros.go
Expand Up @@ -4,17 +4,26 @@
package report

import (
"bufio"
"bytes"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/google/syzkaller/pkg/symbolizer"
)

type akaros struct {
ignores []*regexp.Regexp
objfile string
}

func ctorAkaros(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter, []string, error) {
ctx := &akaros{
ignores: ignores,
objfile: filepath.Join(kernelObj, "akaros-kernel-64b"),
}
return ctx, nil, nil
}
Expand Down Expand Up @@ -52,34 +61,126 @@ func (ctx *akaros) Parse(output []byte) *Report {
if oops == nil {
return nil
}
title, corrupted, _ := extractDescription(output[rep.StartPos:], oops, nil)
title, corrupted, _ := extractDescription(output[rep.StartPos:], oops, akarosStackParams)
rep.Title = title
rep.Report = output[rep.StartPos:]
rep.Report = ctx.minimizeReport(output[rep.StartPos:])
rep.Corrupted = corrupted != ""
rep.corruptedReason = corrupted
rep.CorruptedReason = corrupted
return rep
}

func (ctx *akaros) Symbolize(rep *Report) error {
symb := symbolizer.NewSymbolizer()
defer symb.Close()
var symbolized []byte
s := bufio.NewScanner(bytes.NewReader(rep.Report))
for s.Scan() {
line := bytes.Trim(s.Bytes(), "\r")
line = ctx.symbolizeLine(symb.Symbolize, ctx.objfile, line)
symbolized = append(symbolized, line...)
symbolized = append(symbolized, '\n')
}
rep.Report = symbolized
return nil
}

// kernel panic at kern/src/vfs.c:1359, from core 1: assertion failed: buf == buf_end
// kernel panic at kern/src/ns/sysfile.c:719, from core 1: assertion failed: n >= sizeof(struct kdirent)
/// $ kernel panic at kern/src/slab.c:518, from core 1: [German Accent]: OOM for a small slab growth!!!
func (ctx *akaros) symbolizeLine(symbFunc func(bin string, pc uint64) ([]symbolizer.Frame, error),
objfile string, line []byte) []byte {
match := akarosSymbolizeRe.FindSubmatchIndex(line)
if match == nil {
return line
}
addr, err := strconv.ParseUint(string(line[match[2]:match[3]]), 0, 64)
if err != nil {
return line
}
frames, err := symbFunc(objfile, addr-1)
if err != nil || len(frames) == 0 {
return line
}
var symbolized []byte
for i, frame := range frames {
if i != 0 {
symbolized = append(symbolized, '\n')
}
file := frame.File
if pos := strings.LastIndex(file, "/kern/"); pos != -1 {
file = file[pos+6:]
}
modified := append([]byte{}, line...)
modified = append(modified, fmt.Sprintf(" at %v:%v", file, frame.Line)...)
if frame.Inline {
modified = replace(modified, match[4], match[5], []byte(frame.Func))
modified = replace(modified, match[2], match[3], []byte(" [inline] "))
}
symbolized = append(symbolized, modified...)
}
return symbolized
}

func (ctx *akaros) minimizeReport(report []byte) []byte {
out := new(bytes.Buffer)
for s := bufio.NewScanner(bytes.NewReader(report)); s.Scan(); {
line := bytes.Trim(s.Bytes(), "\r")
if len(line) == 0 ||
bytes.Contains(line, []byte("Entering Nanwan's Dungeon")) ||
bytes.Contains(line, []byte("Type 'help' for a list of commands")) {
continue
}
out.Write(line)
out.WriteByte('\n')
}
return out.Bytes()
}

var (
akarosSymbolizeRe = compile(`^#[0-9]+ \[\<(0x[0-9a-f]+)\>\] in ([a-zA-Z0-9_]+)`)
)

var akarosStackParams = &stackParams{
stackStartRes: []*regexp.Regexp{
regexp.MustCompile(`Stack Backtrace on Core [0-9]+:`),
},
frameRes: []*regexp.Regexp{
compile(`^#[0-9]+ {{PC}} in ([a-zA-Z0-9_]+)`),
},
skipPatterns: []string{
"backtrace",
"mon_backtrace",
"monitor",
"_panic",
},
}

var akarosOopses = []*oops{
&oops{
[]byte("kernel panic"),
[]oopsFormat{
{
title: compile("kernel panic .* assertion failed: (.*)"),
fmt: "assertion failed: %[1]v",
noStackTrace: true,
title: compile("kernel panic at {{SRC}}, from core [0-9]+: assertion failed: (.*)"),
fmt: "assertion failed: %[2]v",
stack: &stackFmt{
parts: []*regexp.Regexp{
compile(`Stack Backtrace on Core [0-9]+:`),
parseStackTrace,
},
},
},
{
title: compile("kernel panic at {{SRC}}, from core [0-9]+: (.*)"),
fmt: "kernel panic: %[2]v",
stack: &stackFmt{
parts: []*regexp.Regexp{
compile(`Stack Backtrace on Core [0-9]+:`),
parseStackTrace,
},
},
},
{
title: compile("kernel panic .* from core [0-9]+: (.*)"),
fmt: "kernel panic: %[1]v",
title: compile("kernel panic"),
fmt: "kernel panic",
noStackTrace: true,
corrupted: true,
},
},
[]*regexp.Regexp{},
Expand Down
2 changes: 1 addition & 1 deletion pkg/report/freebsd.go
Expand Up @@ -69,7 +69,7 @@ func (ctx *freebsd) Parse(output []byte) *Report {
title, corrupted, _ := extractDescription(output[rep.StartPos:], oops, freebsdStackParams)
rep.Title = title
rep.Corrupted = corrupted != ""
rep.corruptedReason = corrupted
rep.CorruptedReason = corrupted
return rep
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/report/gvisor.go
Expand Up @@ -67,7 +67,7 @@ func (ctx *gvisor) Parse(output []byte) *Report {
rep.Title = replaceTable(gvisorTitleReplacement, title)
rep.Report = ctx.shortenReport(output[rep.StartPos:])
rep.Corrupted = corrupted != ""
rep.corruptedReason = corrupted
rep.CorruptedReason = corrupted
return rep
}

Expand Down
9 changes: 2 additions & 7 deletions pkg/report/linux.go
Expand Up @@ -246,7 +246,7 @@ func (ctx *linux) Parse(output []byte) *Report {
}
rep.Title = title
rep.Corrupted = corrupted != ""
rep.corruptedReason = corrupted
rep.CorruptedReason = corrupted
// Prepend 5 lines preceding start of the report,
// they can contain additional info related to the report.
for _, prefix := range reportPrefix {
Expand All @@ -255,7 +255,7 @@ func (ctx *linux) Parse(output []byte) *Report {
}
rep.Report = append(rep.Report, report...)
if !rep.Corrupted {
rep.Corrupted, rep.corruptedReason = ctx.isCorrupted(title, report, format)
rep.Corrupted, rep.CorruptedReason = ctx.isCorrupted(title, report, format)
}
return rep
}
Expand Down Expand Up @@ -445,10 +445,6 @@ func (ctx *linux) extractFiles(report []byte) []string {
}

func (ctx *linux) isCorrupted(title string, report []byte, format oopsFormat) (bool, string) {
// Check if this crash format is marked as corrupted.
if format.corrupted {
return true, "report format is marked as corrupted"
}
// Check if the report contains stack trace.
if !format.noStackTrace && !bytes.Contains(report, []byte("Call Trace")) &&
!bytes.Contains(report, []byte("backtrace")) {
Expand Down Expand Up @@ -496,7 +492,6 @@ func (ctx *linux) isCorrupted(title string, report []byte, format oopsFormat) (b
}

var (
filenameRe = regexp.MustCompile(`[a-zA-Z0-9_\-\./]*[a-zA-Z0-9_\-]+\.(c|h):[0-9]+`)
linuxSymbolizeRe = regexp.MustCompile(`(?:\[\<(?:[0-9a-f]+)\>\])?[ \t]+(?:[0-9]+:)?([a-zA-Z0-9_.]+)\+0x([0-9a-f]+)/0x([0-9a-f]+)`)
stackFrameRe = regexp.MustCompile(`^ *(?:\[\<(?:[0-9a-f]+)\>\])?[ \t]+(?:[0-9]+:)?([a-zA-Z0-9_.]+)\+0x([0-9a-f]+)/0x([0-9a-f]+)`)
linuxRcuStall = compile("INFO: rcu_(?:preempt|sched|bh) (?:self-)?detected(?: expedited)? stall")
Expand Down
17 changes: 12 additions & 5 deletions pkg/report/report.go
Expand Up @@ -41,8 +41,8 @@ type Report struct {
Suppressed bool
// Corrupted indicates whether the report is truncated of corrupted in some other way.
Corrupted bool
// corruptedReason contains reason why the report is marked as corrupted.
corruptedReason string
// CorruptedReason contains reason why the report is marked as corrupted.
CorruptedReason string
// Maintainers is list of maintainer emails.
Maintainers []string
}
Expand Down Expand Up @@ -239,7 +239,7 @@ var parseStackTrace *regexp.Regexp

func compile(re string) *regexp.Regexp {
re = strings.Replace(re, "{{ADDR}}", "0x[0-9a-f]+", -1)
re = strings.Replace(re, "{{PC}}", "\\[\\<[0-9a-f]+\\>\\]", -1)
re = strings.Replace(re, "{{PC}}", "\\[\\<(?:0x)?[0-9a-f]+\\>\\]", -1)
re = strings.Replace(re, "{{FUNC}}", "([a-zA-Z0-9_]+)(?:\\.|\\+)", -1)
re = strings.Replace(re, "{{SRC}}", "([a-zA-Z0-9-_/.]+\\.[a-z]+:[0-9]+)", -1)
return regexp.MustCompile(re)
Expand Down Expand Up @@ -341,6 +341,9 @@ func extractDescription(output []byte, oops *oops, params *stackParams) (
}
desc = string(output[pos:end])
}
if corrupted == "" && format.corrupted {
corrupted = "report format is marked as corrupted"
}
return
}

Expand Down Expand Up @@ -378,7 +381,7 @@ nextPart:
for _, part := range parts {
if part == parseStackTrace {
for s.Scan() {
ln := s.Bytes()
ln := bytes.Trim(s.Bytes(), "\r")
if corrupted == "" && matchesAny(ln, params.corruptedLines) {
corrupted = "corrupted line in report (1)"
}
Expand All @@ -402,7 +405,7 @@ nextPart:
}
} else {
for s.Scan() {
ln := s.Bytes()
ln := bytes.Trim(s.Bytes(), "\r")
if corrupted == "" && matchesAny(ln, params.corruptedLines) {
corrupted = "corrupted line in report (2)"
}
Expand Down Expand Up @@ -445,3 +448,7 @@ func replace(where []byte, start, end int, what []byte) []byte {
}
return where
}

var (
filenameRe = regexp.MustCompile(`[a-zA-Z0-9_\-\./]*[a-zA-Z0-9_\-]+\.(c|h):[0-9]+`)
)
2 changes: 1 addition & 1 deletion pkg/report/report_test.go
Expand Up @@ -139,7 +139,7 @@ func testParseImpl(t *testing.T, reporter Reporter, test *ParseTest) {
if rep != nil {
title = rep.Title
corrupted = rep.Corrupted
corruptedReason = rep.corruptedReason
corruptedReason = rep.CorruptedReason
suppressed = rep.Suppressed
}
if title != test.Title || corrupted != test.Corrupted || suppressed != test.Suppressed {
Expand Down
1 change: 1 addition & 0 deletions pkg/report/testdata/akaros/report/0
@@ -1,4 +1,5 @@
TITLE: kernel panic: [German Accent]: OOM for a small slab growth!!!
CORRUPTED: Y

/ $ kernel panic at kern/src/slab.c:518, from core 1: [German Accent]: OOM for a small slab growth!!!

Expand Down
1 change: 1 addition & 0 deletions pkg/report/testdata/akaros/report/1
@@ -1,4 +1,5 @@
TITLE: kernel panic: Proc-ful Page Fault in the Kernel at ADDR!
CORRUPTED: Y

HW TRAP frame at 0xfffffff00006ab90 on core 0
rax 0xffff8000044a0b60
Expand Down
31 changes: 18 additions & 13 deletions pkg/report/testdata/akaros/report/2
@@ -1,15 +1,20 @@
TITLE: assertion failed: buf == buf_end
TITLE: assertion failed: rpi->gp_acked + 1 == READ_ONCE(rsp->gpnum)

kernel panic at kern/src/rcu.c:325, from core 3: assertion failed: rpi->gp_acked + 1 == READ_ONCE(rsp->gpnum)

Stack Backtrace on Core 3:

#01 [<0xffffffffc200a3b7>] in backtrace

#02 [<0xffffffffc2009b7c>] in _panic

#03 [<0xffffffffc20502d9>] in rcu_report_qs_rpi

#04 [<0xffffffffc2050c2c>] in rcu_report_qs

#05 [<0xffffffffc2054343>] in __smp_idle


Entering Nanwan's Dungeon on Core 3 (Ints off):

kernel panic at kern/src/vfs.c:1359, from core 1: assertion failed: buf == buf_end
Entering Nanwan's Dungeon on Core 1 (Ints on):
Type 'help' for a list of commands.
ROS(Core 1)> bash-4.3$ bt
Stack Backtrace on Core 1:
#01 [<0xffffffffc2016074>] in mon_backtrace
#02 [<0xffffffffc2017177>] in monitor
#03 [<0xffffffffc200cbfc>] in _panic
#04 [<0xffffffffc205c51c>] in generic_file_write
#05 [<0xffffffffc2053890>] in sys_write
#06 [<0xffffffffc2056919>] in syscall
#07 [<0xffffffffc2056ad4>] in run_local_syscall
#08 [<0xffffffffc20a28aa>] in sysenter_callwrapper
1 change: 1 addition & 0 deletions pkg/report/testdata/akaros/report/3
@@ -1,4 +1,5 @@
TITLE: assertion failed: page && pm_slot_check_refcnt(*page->pg_tree_slot)
CORRUPTED: Y

Unhandled user trap in vcore context from VC 1
HW TRAP frame (partial) at 0xffffffffc82cbd20 on core 1
Expand Down
1 change: 1 addition & 0 deletions pkg/report/testdata/akaros/report/4
@@ -1,4 +1,5 @@
TITLE: assertion failed: n >= sizeof(struct kdirent)
CORRUPTED: Y

kernel panic at kern/src/ns/sysfile.c:719, from core 1: assertion failed: n >= sizeof(struct kdirent)

Expand Down
44 changes: 44 additions & 0 deletions pkg/report/testdata/akaros/report/5
@@ -0,0 +1,44 @@
TITLE: assertion failed: !pte_is_mapped(pte)

kernel panic at kern/src/mm.c:737, from core 1: assertion failed: !pte_is_mapped(pte)

Stack Backtrace on Core 1:

#01 [<0xffffffffc200a3b7>] in backtrace

#02 [<0xffffffffc2009b7c>] in _panic

#03 [<0xffffffffc200db41>] in map_page_at_addr

#04 [<0xffffffffc200db92>] in populate_anon_va

#05 [<0xffffffffc200fbf2>] in populate_va

#06 [<0xffffffffc20560f0>] in sys_populate_va

#07 [<0xffffffffc2059129>] in syscall

#08 [<0xffffffffc20592e4>] in run_local_syscall

#09 [<0xffffffffc2059819>] in prep_syscalls

#10 [<0xffffffffc20aaefa>] in sysenter_callwrapper


Entering Nanwan's Dungeon on Core 1 (Ints on):

Type 'help' for a list of commands.

REPORT:
kernel panic at kern/src/mm.c:737, from core 1: assertion failed: !pte_is_mapped(pte)
Stack Backtrace on Core 1:
#01 [<0xffffffffc200a3b7>] in backtrace
#02 [<0xffffffffc2009b7c>] in _panic
#03 [<0xffffffffc200db41>] in map_page_at_addr
#04 [<0xffffffffc200db92>] in populate_anon_va
#05 [<0xffffffffc200fbf2>] in populate_va
#06 [<0xffffffffc20560f0>] in sys_populate_va
#07 [<0xffffffffc2059129>] in syscall
#08 [<0xffffffffc20592e4>] in run_local_syscall
#09 [<0xffffffffc2059819>] in prep_syscalls
#10 [<0xffffffffc20aaefa>] in sysenter_callwrapper

0 comments on commit bad4246

Please sign in to comment.