aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/report/linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/report/linux.go')
-rw-r--r--pkg/report/linux.go207
1 files changed, 114 insertions, 93 deletions
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index 8d493cf1d..2a2500d88 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -27,6 +27,7 @@ type linux struct {
ignores []*regexp.Regexp
consoleOutputRe *regexp.Regexp
questionableRe *regexp.Regexp
+ taskContext *regexp.Regexp
guiltyFileBlacklist []*regexp.Regexp
reportStartIgnores []*regexp.Regexp
infoMessagesWithStack [][]byte
@@ -51,8 +52,9 @@ func ctorLinux(target *targets.Target, kernelSrc, kernelObj string, ignores []*r
symbols: symbols,
ignores: ignores,
}
- ctx.consoleOutputRe = regexp.MustCompile(`^(?:\*\* [0-9]+ printk messages dropped \*\* )?(?:.* login: )?(?:\<[0-9]+\>)?\[ *[0-9]+\.[0-9]+\] `)
- ctx.questionableRe = regexp.MustCompile(`(?:\[\<[0-9a-f]+\>\])? \? +[a-zA-Z0-9_.]+\+0x[0-9a-f]+/[0-9a-f]+`)
+ ctx.consoleOutputRe = regexp.MustCompile(`^(?:\*\* [0-9]+ printk messages dropped \*\* )?(?:.* login: )?(?:\<[0-9]+\>)?\[ *[0-9]+\.[0-9]+\](\[ *(?:C|T)[0-9]+\])? `)
+ ctx.questionableRe = regexp.MustCompile(`(\[\<[0-9a-f]+\>\])? \? +[a-zA-Z0-9_.]+\+0x[0-9a-f]+/[0-9a-f]+`)
+ ctx.taskContext = regexp.MustCompile(`\[ *T[0-9]+\]`)
ctx.eoi = []byte("<EOI>")
ctx.guiltyFileBlacklist = []*regexp.Regexp{
regexp.MustCompile(`.*\.h`),
@@ -126,42 +128,31 @@ func (ctx *linux) ContainsCrash(output []byte) bool {
}
func (ctx *linux) Parse(output []byte) *Report {
- oops, startPos, endPos, logReport, consoleReport, consoleReportReliable,
- logReportPrefix, consoleReportPrefix := ctx.parseOutput(output)
+ oops, startPos, context := ctx.findFirstOops(output)
if oops == nil {
return nil
}
rep := &Report{
Output: output,
StartPos: startPos,
- EndPos: endPos,
}
- var report []byte
- var reportPrefix [][]byte
- // Try extracting report from console output only.
- title, corrupted, format := extractDescription(consoleReportReliable, oops, linuxStackParams)
- if title != "" {
- report = consoleReport
- reportPrefix = consoleReportPrefix
- } else {
- // Failure. Try extracting report from the whole log.
- report = logReport
- reportPrefix = logReportPrefix
+ endPos, reportEnd, report, prefix := ctx.findReport(output, oops, startPos, context)
+ rep.EndPos = endPos
+ title, corrupted, format := extractDescription(report[:reportEnd], oops, linuxStackParams)
+ if title == "" {
+ prefix = nil
+ report = output[rep.StartPos:rep.EndPos]
title, corrupted, format = extractDescription(report, oops, linuxStackParams)
if title == "" {
- panic(fmt.Sprintf("non matching oops for %q in:\n%s\n\nconsole:\n%s\n"+
- "output [range:%v-%v]:\n%s\n",
- oops.header, report, consoleReportReliable,
- rep.StartPos, rep.StartPos+len(report), output))
+ panic(fmt.Sprintf("non matching oops for %q context=%q in:\n%s\n",
+ oops.header, context, report))
}
}
rep.Title = title
rep.Corrupted = 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 {
- rep.Report = append(rep.Report, prefix...)
+ for _, line := range prefix {
+ rep.Report = append(rep.Report, line...)
rep.Report = append(rep.Report, '\n')
}
rep.reportPrefixLen = len(rep.Report)
@@ -172,28 +163,67 @@ func (ctx *linux) Parse(output []byte) *Report {
return rep
}
+func (ctx *linux) findFirstOops(output []byte) (oops *oops, startPos int, context string) {
+ for pos, next := 0, 0; pos < len(output); pos = next + 1 {
+ next = bytes.IndexByte(output[pos:], '\n')
+ if next != -1 {
+ next += pos
+ } else {
+ next = len(output)
+ }
+ line := output[pos:next]
+ for _, oops1 := range linuxOopses {
+ if matchOops(line, oops1, ctx.ignores) {
+ oops = oops1
+ startPos = pos
+ context = ctx.extractContext(line)
+
+ //stripped, questionable := ctx.stripLinePrefix(line, context1)
+
+ return
+ }
+ }
+ }
+ return
+}
+
// Yes, it is complex, but all state and logic are tightly coupled. It's unclear how to simplify it.
// nolint: gocyclo
-func (ctx *linux) parseOutput(output []byte) (
- oops *oops, startPos, endPos int,
- logReport, consoleReport, consoleReportReliable []byte,
- logReportPrefix, consoleReportPrefix [][]byte) {
- firstReportEnd := 0
+func (ctx *linux) findReport(output []byte, oops *oops, startPos int, context string) (
+ endPos, reportEnd int, report []byte, prefix [][]byte) {
+ // Prepend 5 lines preceding start of the report,
+ // they can contain additional info related to the report.
+ maxPrefix := 5
+ if ctx.taskContext.MatchString(context) {
+ // If we have CONFIG_PRINTK_CALLER, we collect more b/c it comes from the same task.
+ maxPrefix = 50
+ }
secondReportPos := 0
textLines := 0
skipText := false
- for pos := 0; pos < len(output); {
- next := bytes.IndexByte(output[pos:], '\n')
+ for pos, next := 0, 0; pos < len(output); pos = next + 1 {
+ next = bytes.IndexByte(output[pos:], '\n')
if next != -1 {
next += pos
} else {
next = len(output)
}
line := output[pos:next]
+ context1 := ctx.extractContext(line)
+ stripped, questionable := ctx.stripLinePrefix(line, context1)
+ if pos < startPos {
+ if context1 == context && len(stripped) != 0 && !questionable {
+ prefix = append(prefix, append([]byte{}, stripped...))
+ if len(prefix) > maxPrefix {
+ prefix = prefix[1:]
+ }
+ }
+ continue
+ }
+ oopsLine := pos == startPos
for _, oops1 := range linuxOopses {
- match := matchOops(line, oops1, ctx.ignores)
- if match == -1 {
- if oops != nil && secondReportPos == 0 {
+ if !matchOops(line, oops1, ctx.ignores) {
+ if !oopsLine && secondReportPos == 0 {
for _, pattern := range ctx.infoMessagesWithStack {
if bytes.Contains(line, pattern) {
secondReportPos = pos
@@ -204,75 +234,66 @@ func (ctx *linux) parseOutput(output []byte) (
continue
}
endPos = next
- if oops == nil {
- oops = oops1
- startPos = pos
- break
- } else if secondReportPos == 0 {
+ if !oopsLine && secondReportPos == 0 {
if !matchesAny(line, ctx.reportStartIgnores) {
secondReportPos = pos
}
}
}
- if oops == nil {
- logReportPrefix = append(logReportPrefix, append([]byte{}, line...))
- if len(logReportPrefix) > 5 {
- logReportPrefix = logReportPrefix[1:]
- }
+ if !oopsLine && (context1 != context || questionable) {
+ continue
}
- if ctx.consoleOutputRe.Match(line) &&
- (!ctx.questionableRe.Match(line) || bytes.Contains(line, ctx.eoi)) {
- lineStart := bytes.Index(line, []byte("] ")) + pos + 2
- lineEnd := next
- if lineEnd != 0 && output[lineEnd-1] == '\r' {
- lineEnd--
- }
- if oops == nil {
- consoleReportPrefix = append(consoleReportPrefix,
- append([]byte{}, output[lineStart:lineEnd]...))
- if len(consoleReportPrefix) > 5 {
- consoleReportPrefix = consoleReportPrefix[1:]
- }
- } else {
- textLines++
- ln := output[lineStart:lineEnd]
- skipLine := skipText
- if bytes.Contains(ln, []byte("Disabling lock debugging due to kernel taint")) {
- skipLine = true
- } else if textLines > 25 &&
- bytes.Contains(ln, []byte("Kernel panic - not syncing")) {
- // If panic_on_warn set, then we frequently have 2 stacks:
- // one for the actual report (or maybe even more than one),
- // and then one for panic caused by panic_on_warn. This makes
- // reports unnecessary long and the panic (current) stack
- // is always present in the actual report. So we strip the
- // panic message. However, we check that we have enough lines
- // before the panic, because sometimes we have, for example,
- // a single WARNING line without a stack and then the panic
- // with the stack.
- skipText = true
- skipLine = true
- }
- if !skipLine {
- consoleReport = append(consoleReport, ln...)
- consoleReport = append(consoleReport, '\n')
- if secondReportPos == 0 {
- firstReportEnd = len(consoleReport)
- }
- }
- }
+ textLines++
+ skipLine := skipText
+ if bytes.Contains(line, []byte("Disabling lock debugging due to kernel taint")) {
+ skipLine = true
+ } else if textLines > 25 &&
+ bytes.Contains(line, []byte("Kernel panic - not syncing")) {
+ // If panic_on_warn set, then we frequently have 2 stacks:
+ // one for the actual report (or maybe even more than one),
+ // and then one for panic caused by panic_on_warn. This makes
+ // reports unnecessary long and the panic (current) stack
+ // is always present in the actual report. So we strip the
+ // panic message. However, we check that we have enough lines
+ // before the panic, because sometimes we have, for example,
+ // a single WARNING line without a stack and then the panic
+ // with the stack.
+ skipText = true
+ skipLine = true
+ }
+ if !oopsLine && skipLine {
+ continue
+ }
+ report = append(report, stripped...)
+ report = append(report, '\n')
+ if secondReportPos == 0 {
+ reportEnd = len(report)
}
- pos = next + 1
}
- if oops == nil {
- return
+ return
+}
+
+func (ctx *linux) stripLinePrefix(line []byte, context string) ([]byte, bool) {
+ if last := len(line) - 1; last >= 0 && line[last] == '\r' {
+ line = line[:last]
}
- if secondReportPos == 0 {
- secondReportPos = len(output)
+ if context == "" {
+ return line, false
}
- logReport = output[startPos:secondReportPos]
- consoleReportReliable = consoleReport[:firstReportEnd]
- return
+ questionable := ctx.questionableRe.Match(line) && !bytes.Contains(line, ctx.eoi)
+ start := bytes.Index(line, []byte("] ")) + 2
+ return line[start:], questionable
+}
+
+func (ctx *linux) extractContext(line []byte) string {
+ match := ctx.consoleOutputRe.FindSubmatchIndex(line)
+ if match == nil {
+ return ""
+ }
+ if match[2] == -1 {
+ return "console"
+ }
+ return string(line[match[2]:match[3]])
}
func (ctx *linux) Symbolize(rep *Report) error {