Skip to content

Commit 8632fe9

Browse files
committed
pkg/report: get only the thread local reports
1 parent ecefd06 commit 8632fe9

File tree

7 files changed

+772
-218
lines changed

7 files changed

+772
-218
lines changed

pkg/report/linux.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,9 @@ func (ctx *linux) Parse(output []byte) *Report {
165165
}
166166
for questionable := false; ; questionable = true {
167167
rep := &Report{
168-
Output: output,
169-
StartPos: startPos,
168+
Output: output,
169+
StartPos: startPos,
170+
ContextID: context,
170171
}
171172
endPos, reportEnd, report, prefix := ctx.findReport(output, oops, startPos, context, questionable)
172173
rep.EndPos = endPos

pkg/report/report.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ type Report struct {
7272
MachineInfo []byte
7373
// If the crash happened in the context of the syz-executor process, Executor will hold more info.
7474
Executor *ExecutorInfo
75+
// On Linux systems ContextID may be the ThreadID(enabled by CONFIG_PRINTK_CALLER)
76+
// or alternatively CpuID.
77+
ContextID string
78+
7579
// reportPrefixLen is length of additional prefix lines that we added before actual crash report.
7680
reportPrefixLen int
7781
// symbolized is set if the report is symbolized. It prevents double symbolization.
@@ -277,17 +281,44 @@ func IsSuppressed(reporter *Reporter, output []byte) bool {
277281
bytes.Contains(output, gceConsoleHangup)
278282
}
279283

280-
// ParseAll returns all successive reports in output.
281-
func ParseAll(reporter *Reporter, output []byte) (reports []*Report) {
282-
skipPos := 0
284+
// ParseAll returns all successive reports in output starting from startFrom.
285+
func ParseAll(reporter *Reporter, output []byte, startFrom int) []*Report {
286+
skipPos := startFrom
287+
var res []*Report
288+
var scanFrom []int
283289
for {
284290
rep := reporter.ParseFrom(output, skipPos)
285291
if rep == nil {
286-
return
292+
break
287293
}
288-
reports = append(reports, rep)
294+
res = append(res, rep)
295+
scanFrom = append(scanFrom, skipPos)
289296
skipPos = rep.SkipPos
290297
}
298+
return fixReports(reporter, res, scanFrom)
299+
}
300+
301+
func fixReports(reporter *Reporter, reports []*Report, skipPos []int) []*Report {
302+
nextContextReportPos := map[string]int{}
303+
for i := len(reports) - 1; i >= 0; i-- {
304+
rep := reports[i]
305+
if rep.Corrupted {
306+
continue
307+
}
308+
nextReportPos := nextContextReportPos[rep.ContextID]
309+
nextContextReportPos[rep.ContextID] = rep.StartPos
310+
if nextReportPos == 0 {
311+
continue
312+
}
313+
if nextReportPos < rep.EndPos {
314+
shorterReport := reporter.ParseFrom(rep.Output[:nextReportPos], skipPos[i])
315+
if !shorterReport.Corrupted {
316+
reports[i] = reporter.ParseFrom(rep.Output[:nextReportPos], skipPos[i])
317+
reports[i].Output = rep.Output
318+
}
319+
}
320+
}
321+
return reports
291322
}
292323

293324
// GCE console connection sometimes fails with this message.
@@ -944,13 +975,15 @@ func TitleToCrashType(title string) crash.Type {
944975
return crash.UnknownType
945976
}
946977

947-
const reportSeparator = "\n<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>\n\n"
978+
const reportSeparator = "<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>"
948979

949980
func MergeReportBytes(reps []*Report) []byte {
950981
var res []byte
951-
for _, rep := range reps {
982+
for i, rep := range reps {
983+
if i > 0 {
984+
res = append(res, []byte(reportSeparator)...)
985+
}
952986
res = append(res, rep.Report...)
953-
res = append(res, []byte(reportSeparator)...)
954987
}
955988
return res
956989
}

pkg/report/report_test.go

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,21 @@ func TestParse(t *testing.T) {
3131
}
3232

3333
type ParseTest struct {
34-
FileName string
35-
Log []byte
36-
Title string
37-
AltTitles []string
38-
Type crash.Type
39-
Frame string
40-
StartLine string
41-
EndLine string
42-
Corrupted bool
43-
Suppressed bool
44-
HasReport bool
45-
Report []byte
46-
Executor string
34+
FileName string
35+
Log []byte
36+
Title string
37+
AltTitles []string
38+
Type crash.Type
39+
Frame string
40+
StartLine string
41+
EndLine string
42+
Corrupted bool
43+
Suppressed bool
44+
HasReport bool
45+
Report []byte
46+
TailReports [][]byte
47+
Executor string
48+
ContextIDs []string
4749
// Only used in report parsing:
4850
corruptedReason string
4951
}
@@ -64,6 +66,9 @@ func (test *ParseTest) Equal(other *ParseTest) bool {
6466
if test.HasReport && !bytes.Equal(test.Report, other.Report) {
6567
return false
6668
}
69+
if test.HasReport && !reflect.DeepEqual(test.TailReports, other.TailReports) {
70+
return false
71+
}
6772
return test.Executor == other.Executor
6873
}
6974

@@ -90,6 +95,9 @@ func (test *ParseTest) Headers() []byte {
9095
if test.Executor != "" {
9196
fmt.Fprintf(buf, "EXECUTOR: %s\n", test.Executor)
9297
}
98+
if len(test.ContextIDs) != 0 {
99+
fmt.Fprintf(buf, "CONTEXTS: %v\n", test.ContextIDs)
100+
}
93101
return buf.Bytes()
94102
}
95103

@@ -98,8 +106,8 @@ func testParseFile(t *testing.T, reporter *Reporter, fn string) {
98106
testParseImpl(t, reporter, test)
99107
}
100108

101-
func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
102-
data, err := os.ReadFile(fn)
109+
func parseReport(t *testing.T, reporter *Reporter, testFileName string) *ParseTest {
110+
data, err := os.ReadFile(testFileName)
103111
if err != nil {
104112
t.Fatal(err)
105113
}
@@ -109,10 +117,11 @@ func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
109117
phaseHeaders = iota
110118
phaseLog
111119
phaseReport
120+
phaseTailReports
112121
)
113122
phase := phaseHeaders
114123
test := &ParseTest{
115-
FileName: fn,
124+
FileName: testFileName,
116125
}
117126
prevEmptyLine := false
118127
s := bufio.NewScanner(bytes.NewReader(data))
@@ -134,8 +143,20 @@ func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
134143
test.Log = append(test.Log, '\n')
135144
}
136145
case phaseReport:
137-
test.Report = append(test.Report, s.Bytes()...)
138-
test.Report = append(test.Report, '\n')
146+
if string(s.Bytes()) == "TAIL REPORTS:" {
147+
test.TailReports = [][]byte{{}}
148+
phase = phaseTailReports
149+
} else {
150+
test.Report = append(test.Report, s.Bytes()...)
151+
test.Report = append(test.Report, '\n')
152+
}
153+
case phaseTailReports:
154+
if string(s.Bytes()) == reportSeparator {
155+
test.TailReports = append(test.TailReports, []byte{})
156+
continue
157+
}
158+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], s.Bytes()...)
159+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], []byte{'\n'}...)
139160
}
140161
prevEmptyLine = len(s.Bytes()) == 0
141162
}
@@ -160,6 +181,7 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
160181
corruptedPrefix = "CORRUPTED: "
161182
suppressedPrefix = "SUPPRESSED: "
162183
executorPrefix = "EXECUTOR: "
184+
contextidPrefix = "CONTEXTS: "
163185
)
164186
switch {
165187
case strings.HasPrefix(ln, "#"):
@@ -195,60 +217,72 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
195217
}
196218
case strings.HasPrefix(ln, executorPrefix):
197219
test.Executor = ln[len(executorPrefix):]
220+
case strings.HasPrefix(ln, contextidPrefix):
221+
test.ContextIDs = strings.Split(ln[len(contextidPrefix):], ", ")
198222
default:
199223
t.Fatalf("unknown header field %q", ln)
200224
}
201225
}
202226

203-
func testFromReport(rep *Report) *ParseTest {
204-
if rep == nil {
227+
func testFromReports(reps ...*Report) *ParseTest {
228+
if reps == nil || len(reps) > 0 && reps[0] == nil {
205229
return &ParseTest{}
206230
}
207231
ret := &ParseTest{
208-
Title: rep.Title,
209-
AltTitles: rep.AltTitles,
210-
Corrupted: rep.Corrupted,
211-
corruptedReason: rep.CorruptedReason,
212-
Suppressed: rep.Suppressed,
213-
Type: TitleToCrashType(rep.Title),
214-
Frame: rep.Frame,
215-
Report: rep.Report,
216-
}
217-
if rep.Executor != nil {
218-
ret.Executor = fmt.Sprintf("proc=%d, id=%d", rep.Executor.ProcID, rep.Executor.ExecID)
232+
Title: reps[0].Title,
233+
AltTitles: reps[0].AltTitles,
234+
Corrupted: reps[0].Corrupted,
235+
corruptedReason: reps[0].CorruptedReason,
236+
Suppressed: reps[0].Suppressed,
237+
Type: TitleToCrashType(reps[0].Title),
238+
Frame: reps[0].Frame,
239+
Report: reps[0].Report,
240+
}
241+
if reps[0].Executor != nil {
242+
ret.Executor = fmt.Sprintf("proc=%d, id=%d", reps[0].Executor.ProcID, reps[0].Executor.ExecID)
219243
}
220244
sort.Strings(ret.AltTitles)
245+
ret.ContextIDs = append(ret.ContextIDs, reps[0].ContextID)
246+
for i := 1; i < len(reps); i++ {
247+
ret.TailReports = append(ret.TailReports, reps[i].Report)
248+
ret.ContextIDs = append(ret.ContextIDs, reps[i].ContextID)
249+
}
221250
return ret
222251
}
223252

224253
func testParseImpl(t *testing.T, reporter *Reporter, test *ParseTest) {
225-
rep := reporter.Parse(test.Log)
254+
gotReports := ParseAll(reporter, test.Log, 0)
255+
256+
var firstReport *Report
257+
if len(gotReports) > 0 {
258+
firstReport = gotReports[0]
259+
}
226260
containsCrash := reporter.ContainsCrash(test.Log)
227261
expectCrash := (test.Title != "")
228262
if expectCrash && !containsCrash {
229263
t.Fatalf("did not find crash")
230264
}
231265
if !expectCrash && containsCrash {
232-
t.Fatalf("found unexpected crash")
266+
t.Fatalf("found unexpected crash: %s", firstReport.Title)
233267
}
234-
if rep != nil && rep.Title == "" {
268+
if firstReport != nil && firstReport.Title == "" {
235269
t.Fatalf("found crash, but title is empty")
236270
}
237-
parsed := testFromReport(rep)
271+
parsed := testFromReports(gotReports...)
238272
if !test.Equal(parsed) {
239273
if *flagUpdate && test.StartLine+test.EndLine == "" {
240274
updateReportTest(t, test, parsed)
241275
}
242276
t.Fatalf("want:\n%s\ngot:\n%sCorrupted reason: %q",
243277
test.Headers(), parsed.Headers(), parsed.corruptedReason)
244278
}
245-
if parsed.Title != "" && len(rep.Report) == 0 {
279+
if parsed.Title != "" && len(firstReport.Report) == 0 {
246280
t.Fatalf("found crash message but report is empty")
247281
}
248-
if rep == nil {
282+
if firstReport == nil {
249283
return
250284
}
251-
checkReport(t, reporter, rep, test)
285+
checkReport(t, reporter, firstReport, test)
252286
}
253287

254288
func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest) {
@@ -285,11 +319,6 @@ func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest)
285319
if rep1 == nil || rep1.Title != rep.Title || rep1.StartPos != rep.StartPos {
286320
t.Fatalf("did not find the same report from rep.StartPos=%v", rep.StartPos)
287321
}
288-
// If we parse from EndPos, we must not find the same report.
289-
rep2 := reporter.ParseFrom(test.Log, rep.EndPos)
290-
if rep2 != nil && rep2.Title == rep.Title {
291-
t.Fatalf("found the same report after rep.EndPos=%v", rep.EndPos)
292-
}
293322
}
294323
}
295324

@@ -303,7 +332,13 @@ func updateReportTest(t *testing.T, test, parsed *ParseTest) {
303332
fmt.Fprintf(buf, "\n%s", test.Log)
304333
if test.HasReport {
305334
fmt.Fprintf(buf, "REPORT:\n%s", parsed.Report)
335+
336+
if len(parsed.TailReports) > 0 {
337+
fmt.Fprintf(buf, "TAIL REPORTS:\n")
338+
buf.Write(bytes.Join(parsed.TailReports, []byte(reportSeparator+"\n")))
339+
}
306340
}
341+
307342
if err := os.WriteFile(test.FileName, buf.Bytes(), 0640); err != nil {
308343
t.Logf("failed to update test file: %v", err)
309344
}
@@ -395,7 +430,7 @@ func testSymbolizeFile(t *testing.T, reporter *Reporter, fn string) {
395430
if err != nil {
396431
t.Fatalf("failed to symbolize: %v", err)
397432
}
398-
parsed := testFromReport(rep)
433+
parsed := testFromReports(rep)
399434
if !test.Equal(parsed) {
400435
if *flagUpdate {
401436
updateReportTest(t, test, parsed)

tools/syz-symbolize/symbolize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func main() {
5757
if err != nil {
5858
tool.Failf("failed to open input file: %v", err)
5959
}
60-
reps := report.ParseAll(reporter, text)
60+
reps := report.ParseAll(reporter, text, 0)
6161
if len(reps) == 0 {
6262
rep := &report.Report{Report: text}
6363
if err := reporter.Symbolize(rep); err != nil {

0 commit comments

Comments
 (0)