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