Skip to content

Commit 8dcce27

Browse files
committed
pkg/report: change report generation
1 parent e434897 commit 8dcce27

File tree

5 files changed

+104
-72
lines changed

5 files changed

+104
-72
lines changed

pkg/report/report.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,11 @@ func fixReports(reporter *Reporter, reports []*Report, skipPos []int) []*Report
311311
continue
312312
}
313313
if nextReportPos < rep.EndPos {
314-
reports[i] = reporter.ParseFrom(rep.Output[:nextReportPos], skipPos[i])
315-
reports[i].Output = rep.Output
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+
}
316319
}
317320
}
318321
return reports
@@ -972,13 +975,15 @@ func TitleToCrashType(title string) crash.Type {
972975
return crash.UnknownType
973976
}
974977

975-
const reportSeparator = "\n<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>\n\n"
978+
const reportSeparator = "<<<<<<<<<<<<<<< tail report >>>>>>>>>>>>>>>"
976979

977980
func MergeReportBytes(reps []*Report) []byte {
978981
var res []byte
979-
for _, rep := range reps {
982+
for i, rep := range reps {
983+
if i > 0 {
984+
res = append(res, []byte(reportSeparator)...)
985+
}
980986
res = append(res, rep.Report...)
981-
res = append(res, []byte(reportSeparator)...)
982987
}
983988
return res
984989
}

pkg/report/report_test.go

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +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
47-
ContextIDs 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
4849
// Only used in report parsing:
4950
corruptedReason string
5051
}
@@ -59,12 +60,18 @@ func (test *ParseTest) Equal(other *ParseTest) bool {
5960
if !reflect.DeepEqual(test.AltTitles, other.AltTitles) {
6061
return false
6162
}
63+
// Check frame only if it was present in the test file.
6264
if test.Frame != "" && test.Frame != other.Frame {
6365
return false
6466
}
67+
// Check report only if it was present in the test file.
6568
if test.HasReport && !bytes.Equal(test.Report, other.Report) {
6669
return false
6770
}
71+
// Check tail reports only if they were present in the test file.
72+
if test.HasReport && !reflect.DeepEqual(test.TailReports, other.TailReports) {
73+
return false
74+
}
6875
return test.Executor == other.Executor
6976
}
7077

@@ -91,8 +98,8 @@ func (test *ParseTest) Headers() []byte {
9198
if test.Executor != "" {
9299
fmt.Fprintf(buf, "EXECUTOR: %s\n", test.Executor)
93100
}
94-
if test.ContextIDs != "" {
95-
fmt.Fprintf(buf, "CONTEXTS: %s\n", test.ContextIDs)
101+
if len(test.ContextIDs) != 0 {
102+
fmt.Fprintf(buf, "CONTEXTS: %v\n", test.ContextIDs)
96103
}
97104
return buf.Bytes()
98105
}
@@ -113,6 +120,7 @@ func parseReport(t *testing.T, reporter *Reporter, testFileName string) *ParseTe
113120
phaseHeaders = iota
114121
phaseLog
115122
phaseReport
123+
phaseTailReports
116124
)
117125
phase := phaseHeaders
118126
test := &ParseTest{
@@ -121,6 +129,14 @@ func parseReport(t *testing.T, reporter *Reporter, testFileName string) *ParseTe
121129
prevEmptyLine := false
122130
s := bufio.NewScanner(bytes.NewReader(data))
123131
for s.Scan() {
132+
133+
if string(s.Bytes()) == "TAIL REPORTS:" {
134+
test.HasReport = true // we want both, REPORT and TAIL REPORT to be present
135+
test.TailReports = [][]byte{{}}
136+
phase = phaseTailReports
137+
continue
138+
}
139+
124140
switch phase {
125141
case phaseHeaders:
126142
ln := s.Text()
@@ -140,11 +156,18 @@ func parseReport(t *testing.T, reporter *Reporter, testFileName string) *ParseTe
140156
case phaseReport:
141157
test.Report = append(test.Report, s.Bytes()...)
142158
test.Report = append(test.Report, '\n')
159+
case phaseTailReports:
160+
if string(s.Bytes()) == reportSeparator {
161+
test.TailReports = append(test.TailReports, []byte{})
162+
continue
163+
}
164+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], s.Bytes()...)
165+
test.TailReports[len(test.TailReports)-1] = append(test.TailReports[len(test.TailReports)-1], []byte{'\n'}...)
143166
}
144167
prevEmptyLine = len(s.Bytes()) == 0
145168
}
146169
if s.Err() != nil {
147-
t.Fatalf("file scanning error: %v", s.Err())
170+
t.Fatalf("file scanningerror: %v", s.Err())
148171
}
149172
if len(test.Log) == 0 {
150173
t.Fatalf("can't find log in input file")
@@ -201,61 +224,87 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
201224
case strings.HasPrefix(ln, executorPrefix):
202225
test.Executor = ln[len(executorPrefix):]
203226
case strings.HasPrefix(ln, contextidPrefix):
204-
test.ContextIDs = ln[len(contextidPrefix):]
227+
test.ContextIDs = strings.Split(ln[len(contextidPrefix):], ", ")
205228
default:
206229
t.Fatalf("unknown header field %q", ln)
207230
}
208231
}
209232

210-
func testFromReport(rep *Report) *ParseTest {
211-
if rep == nil {
233+
func testFromReports(reps ...*Report) *ParseTest {
234+
if reps == nil || len(reps) > 0 && reps[0] == nil {
212235
return &ParseTest{}
213236
}
214237
ret := &ParseTest{
215-
Title: rep.Title,
216-
AltTitles: rep.AltTitles,
217-
Corrupted: rep.Corrupted,
218-
corruptedReason: rep.CorruptedReason,
219-
Suppressed: rep.Suppressed,
220-
Type: TitleToCrashType(rep.Title),
221-
Frame: rep.Frame,
222-
Report: rep.Report,
223-
}
224-
if rep.Executor != nil {
225-
ret.Executor = fmt.Sprintf("proc=%d, id=%d", rep.Executor.ProcID, rep.Executor.ExecID)
238+
Title: reps[0].Title,
239+
AltTitles: reps[0].AltTitles,
240+
Corrupted: reps[0].Corrupted,
241+
corruptedReason: reps[0].CorruptedReason,
242+
Suppressed: reps[0].Suppressed,
243+
Type: TitleToCrashType(reps[0].Title),
244+
Frame: reps[0].Frame,
245+
Report: reps[0].Report,
246+
}
247+
if reps[0].Executor != nil {
248+
ret.Executor = fmt.Sprintf("proc=%d, id=%d", reps[0].Executor.ProcID, reps[0].Executor.ExecID)
226249
}
227250
sort.Strings(ret.AltTitles)
251+
ret.ContextIDs = append(ret.ContextIDs, reps[0].ContextID)
252+
for i := 1; i < len(reps); i++ {
253+
ret.TailReports = append(ret.TailReports, reps[i].Report)
254+
ret.ContextIDs = append(ret.ContextIDs, reps[i].ContextID)
255+
}
228256
return ret
229257
}
230258

259+
/*func testParseAll(t *testing.T, reporter *Reporter, testFileName string) {
260+
test := parseReport(t, reporter, testFileName)
261+
gotReports := ParseAll(reporter, test.Log, 0)
262+
gotIDs := mergeReportContextIDs(gotReports)
263+
mergedReport := MergeReportBytes(gotReports)
264+
if !bytes.Equal(mergedReport, test.Report) || gotIDs != test.ContextIDs {
265+
if *flagUpdate {
266+
updateReportTest(t, test, &ParseTest{
267+
ContextIDs: gotIDs,
268+
Report: mergedReport})
269+
}
270+
assert.Equal(t, test.ContextIDs, gotIDs, "extracted wrong Thread or CPU ids")
271+
assert.Equal(t, string(test.Report), string(mergedReport), "extracted wrong reports")
272+
}
273+
}*/
274+
231275
func testParseImpl(t *testing.T, reporter *Reporter, test *ParseTest) {
232-
rep := reporter.Parse(test.Log)
276+
gotReports := ParseAll(reporter, test.Log, 0)
277+
278+
var firstReport *Report
279+
if len(gotReports) > 0 {
280+
firstReport = gotReports[0]
281+
}
233282
containsCrash := reporter.ContainsCrash(test.Log)
234283
expectCrash := (test.Title != "")
235284
if expectCrash && !containsCrash {
236285
t.Fatalf("did not find crash")
237286
}
238287
if !expectCrash && containsCrash {
239-
t.Fatalf("found unexpected crash")
288+
t.Fatalf("found unexpected crash: %s", firstReport.Title)
240289
}
241-
if rep != nil && rep.Title == "" {
290+
if firstReport != nil && firstReport.Title == "" {
242291
t.Fatalf("found crash, but title is empty")
243292
}
244-
parsed := testFromReport(rep)
293+
parsed := testFromReports(gotReports...)
245294
if !test.Equal(parsed) {
246295
if *flagUpdate && test.StartLine+test.EndLine == "" {
247296
updateReportTest(t, test, parsed)
248297
}
249298
t.Fatalf("want:\n%s\ngot:\n%sCorrupted reason: %q",
250299
test.Headers(), parsed.Headers(), parsed.corruptedReason)
251300
}
252-
if parsed.Title != "" && len(rep.Report) == 0 {
301+
if parsed.Title != "" && len(firstReport.Report) == 0 {
253302
t.Fatalf("found crash message but report is empty")
254303
}
255-
if rep == nil {
304+
if firstReport == nil {
256305
return
257306
}
258-
checkReport(t, reporter, rep, test)
307+
checkReport(t, reporter, firstReport, test)
259308
}
260309

261310
func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest) {
@@ -292,11 +341,6 @@ func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest)
292341
if rep1 == nil || rep1.Title != rep.Title || rep1.StartPos != rep.StartPos {
293342
t.Fatalf("did not find the same report from rep.StartPos=%v", rep.StartPos)
294343
}
295-
// If we parse from EndPos, we must not find the same report.
296-
rep2 := reporter.ParseFrom(test.Log, rep.EndPos)
297-
if rep2 != nil && rep2.Title == rep.Title {
298-
t.Fatalf("found the same report after rep.EndPos=%v", rep.EndPos)
299-
}
300344
}
301345
}
302346

@@ -310,7 +354,13 @@ func updateReportTest(t *testing.T, test, parsed *ParseTest) {
310354
fmt.Fprintf(buf, "\n%s", test.Log)
311355
if test.HasReport {
312356
fmt.Fprintf(buf, "REPORT:\n%s", parsed.Report)
357+
358+
if len(parsed.TailReports) > 0 {
359+
fmt.Fprintf(buf, "TAIL REPORTS:\n")
360+
buf.Write(bytes.Join(parsed.TailReports, []byte(reportSeparator+"\n")))
361+
}
313362
}
363+
314364
if err := os.WriteFile(test.FileName, buf.Bytes(), 0640); err != nil {
315365
t.Logf("failed to update test file: %v", err)
316366
}
@@ -402,7 +452,7 @@ func testSymbolizeFile(t *testing.T, reporter *Reporter, fn string) {
402452
if err != nil {
403453
t.Fatalf("failed to symbolize: %v", err)
404454
}
405-
parsed := testFromReport(rep)
455+
parsed := testFromReports(rep)
406456
if !test.Equal(parsed) {
407457
if *flagUpdate {
408458
updateReportTest(t, test, parsed)
@@ -558,26 +608,3 @@ func TestSplitReportBytes(t *testing.T) {
558608
})
559609
}
560610
}
561-
562-
// TestParseAll's focus points are the ability to:
563-
// 1. parse multiple reports
564-
// 2. extract correct ThreadID/CpuID.
565-
func TestParseAll(t *testing.T) {
566-
forEachFile(t, "parse_all", testParseAll)
567-
}
568-
569-
func testParseAll(t *testing.T, reporter *Reporter, testFileName string) {
570-
test := parseReport(t, reporter, testFileName)
571-
gotReports := ParseAll(reporter, test.Log, 0)
572-
gotIDs := mergeReportContextIDs(gotReports)
573-
mergedReport := MergeReportBytes(gotReports)
574-
if !bytes.Equal(mergedReport, test.Report) || gotIDs != test.ContextIDs {
575-
if *flagUpdate {
576-
updateReportTest(t, test, &ParseTest{
577-
ContextIDs: gotIDs,
578-
Report: mergedReport})
579-
}
580-
assert.Equal(t, test.ContextIDs, gotIDs, "extracted wrong Thread or CPU ids")
581-
assert.Equal(t, string(test.Report), string(mergedReport), "extracted wrong reports")
582-
}
583-
}

0 commit comments

Comments
 (0)