Skip to content

Commit 43ef3cf

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

File tree

7 files changed

+772
-216
lines changed

7 files changed

+772
-216
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 & 42 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,14 +117,22 @@ 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))
119128
for s.Scan() {
129+
if string(s.Bytes()) == "TAIL REPORTS:" {
130+
test.HasReport = true // we want both, REPORT and TAIL REPORT to be present
131+
test.TailReports = [][]byte{{}}
132+
phase = phaseTailReports
133+
continue
134+
}
135+
120136
switch phase {
121137
case phaseHeaders:
122138
ln := s.Text()
@@ -136,6 +152,13 @@ func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
136152
case phaseReport:
137153
test.Report = append(test.Report, s.Bytes()...)
138154
test.Report = append(test.Report, '\n')
155+
case phaseTailReports:
156+
if string(s.Bytes()) == reportSeparator {
157+
test.TailReports = append(test.TailReports, []byte{})
158+
continue
159+
}
160+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], s.Bytes()...)
161+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], []byte{'\n'}...)
139162
}
140163
prevEmptyLine = len(s.Bytes()) == 0
141164
}
@@ -160,6 +183,7 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
160183
corruptedPrefix = "CORRUPTED: "
161184
suppressedPrefix = "SUPPRESSED: "
162185
executorPrefix = "EXECUTOR: "
186+
contextidPrefix = "CONTEXTS: "
163187
)
164188
switch {
165189
case strings.HasPrefix(ln, "#"):
@@ -195,60 +219,72 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
195219
}
196220
case strings.HasPrefix(ln, executorPrefix):
197221
test.Executor = ln[len(executorPrefix):]
222+
case strings.HasPrefix(ln, contextidPrefix):
223+
test.ContextIDs = strings.Split(ln[len(contextidPrefix):], ", ")
198224
default:
199225
t.Fatalf("unknown header field %q", ln)
200226
}
201227
}
202228

203-
func testFromReport(rep *Report) *ParseTest {
204-
if rep == nil {
229+
func testFromReports(reps ...*Report) *ParseTest {
230+
if reps == nil || len(reps) > 0 && reps[0] == nil {
205231
return &ParseTest{}
206232
}
207233
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)
234+
Title: reps[0].Title,
235+
AltTitles: reps[0].AltTitles,
236+
Corrupted: reps[0].Corrupted,
237+
corruptedReason: reps[0].CorruptedReason,
238+
Suppressed: reps[0].Suppressed,
239+
Type: TitleToCrashType(reps[0].Title),
240+
Frame: reps[0].Frame,
241+
Report: reps[0].Report,
242+
}
243+
if reps[0].Executor != nil {
244+
ret.Executor = fmt.Sprintf("proc=%d, id=%d", reps[0].Executor.ProcID, reps[0].Executor.ExecID)
219245
}
220246
sort.Strings(ret.AltTitles)
247+
ret.ContextIDs = append(ret.ContextIDs, reps[0].ContextID)
248+
for i := 1; i < len(reps); i++ {
249+
ret.TailReports = append(ret.TailReports, reps[i].Report)
250+
ret.ContextIDs = append(ret.ContextIDs, reps[i].ContextID)
251+
}
221252
return ret
222253
}
223254

224255
func testParseImpl(t *testing.T, reporter *Reporter, test *ParseTest) {
225-
rep := reporter.Parse(test.Log)
256+
gotReports := ParseAll(reporter, test.Log, 0)
257+
258+
var firstReport *Report
259+
if len(gotReports) > 0 {
260+
firstReport = gotReports[0]
261+
}
226262
containsCrash := reporter.ContainsCrash(test.Log)
227263
expectCrash := (test.Title != "")
228264
if expectCrash && !containsCrash {
229265
t.Fatalf("did not find crash")
230266
}
231267
if !expectCrash && containsCrash {
232-
t.Fatalf("found unexpected crash")
268+
t.Fatalf("found unexpected crash: %s", firstReport.Title)
233269
}
234-
if rep != nil && rep.Title == "" {
270+
if firstReport != nil && firstReport.Title == "" {
235271
t.Fatalf("found crash, but title is empty")
236272
}
237-
parsed := testFromReport(rep)
273+
parsed := testFromReports(gotReports...)
238274
if !test.Equal(parsed) {
239275
if *flagUpdate && test.StartLine+test.EndLine == "" {
240276
updateReportTest(t, test, parsed)
241277
}
242278
t.Fatalf("want:\n%s\ngot:\n%sCorrupted reason: %q",
243279
test.Headers(), parsed.Headers(), parsed.corruptedReason)
244280
}
245-
if parsed.Title != "" && len(rep.Report) == 0 {
281+
if parsed.Title != "" && len(firstReport.Report) == 0 {
246282
t.Fatalf("found crash message but report is empty")
247283
}
248-
if rep == nil {
284+
if firstReport == nil {
249285
return
250286
}
251-
checkReport(t, reporter, rep, test)
287+
checkReport(t, reporter, firstReport, test)
252288
}
253289

254290
func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest) {
@@ -285,11 +321,6 @@ func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest)
285321
if rep1 == nil || rep1.Title != rep.Title || rep1.StartPos != rep.StartPos {
286322
t.Fatalf("did not find the same report from rep.StartPos=%v", rep.StartPos)
287323
}
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-
}
293324
}
294325
}
295326

@@ -303,7 +334,13 @@ func updateReportTest(t *testing.T, test, parsed *ParseTest) {
303334
fmt.Fprintf(buf, "\n%s", test.Log)
304335
if test.HasReport {
305336
fmt.Fprintf(buf, "REPORT:\n%s", parsed.Report)
337+
338+
if len(parsed.TailReports) > 0 {
339+
fmt.Fprintf(buf, "TAIL REPORTS:\n")
340+
buf.Write(bytes.Join(parsed.TailReports, []byte(reportSeparator+"\n")))
341+
}
306342
}
343+
307344
if err := os.WriteFile(test.FileName, buf.Bytes(), 0640); err != nil {
308345
t.Logf("failed to update test file: %v", err)
309346
}
@@ -395,7 +432,7 @@ func testSymbolizeFile(t *testing.T, reporter *Reporter, fn string) {
395432
if err != nil {
396433
t.Fatalf("failed to symbolize: %v", err)
397434
}
398-
parsed := testFromReport(rep)
435+
parsed := testFromReports(rep)
399436
if !test.Equal(parsed) {
400437
if *flagUpdate {
401438
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)