1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
// Copyright 2024 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package rpcserver
import (
"bytes"
"fmt"
"sort"
"time"
"github.com/google/syzkaller/pkg/report"
"github.com/google/syzkaller/prog"
)
// LastExecuting keeps the given number of last executed programs
// for each proc in a VM, and allows to query this set after a crash.
type LastExecuting struct {
count int
procs []ExecRecord
hanged []ExecRecord // hanged programs, kept forever
positions []int
}
type ExecRecord struct {
ID int
Proc int
Prog []byte
Time time.Duration
}
func MakeLastExecuting(procs, count int) *LastExecuting {
return &LastExecuting{
count: count,
procs: make([]ExecRecord, procs*count),
positions: make([]int, procs),
}
}
// Note execution of the 'prog' on 'proc' at time 'now'.
func (last *LastExecuting) Note(id, proc int, progData []byte, now time.Duration) {
pos := &last.positions[proc]
last.procs[proc*last.count+*pos] = ExecRecord{
ID: id,
Proc: proc,
Prog: progData,
Time: now,
}
*pos++
if *pos == last.count {
*pos = 0
}
}
// Note a hanged program.
func (last *LastExecuting) Hanged(id, proc int, progData []byte, now time.Duration) {
last.hanged = append(last.hanged, ExecRecord{
ID: id,
// Use unique proc for these programs b/c pkg/repro will either use the program with matching ID,
// of take the last program from each proc, and we want the hanged programs to be included.
Proc: prog.MaxPids + len(last.hanged),
Prog: progData,
Time: now,
})
}
// Returns a sorted set of last executing programs.
// The records are sorted by time in ascending order.
// ExecRecord.Time is the difference in start executing time between this
// program and the program that started executing last.
func (last *LastExecuting) Collect() []ExecRecord {
procs := append(last.procs, last.hanged...)
last.procs = nil // The type must not be used after this.
last.hanged = nil
sort.Slice(procs, func(i, j int) bool {
return procs[i].Time < procs[j].Time
})
max := procs[len(procs)-1].Time
for i := len(procs) - 1; i >= 0; i-- {
if procs[i].Time == 0 {
procs = procs[i+1:]
break
}
procs[i].Time = max - procs[i].Time
}
return procs
}
func PrependExecuting(rep *report.Report, lastExec []ExecRecord) {
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "last executing test programs:\n\n")
for _, exec := range lastExec {
fmt.Fprintf(buf, "%v ago: executing program %v (id=%v):\n%s\n", exec.Time, exec.Proc, exec.ID, exec.Prog)
}
fmt.Fprintf(buf, "kernel console output (not intermixed with test programs):\n\n")
rep.Output = append(buf.Bytes(), rep.Output...)
n := len(buf.Bytes())
rep.StartPos += n
rep.EndPos += n
rep.SkipPos += n
}
|