mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 11:29:46 +00:00
syz-ci: extend build info
We currently store 3 tags (compiler id, kernel commit and config hash). But we also kernel git report/branch. To not store 2 more tag files, combine everything into a single json file that holds all info about the build. Will allow simpler extenstion in future as well.
This commit is contained in:
parent
51a013e7b9
commit
cc9db10249
@ -30,10 +30,8 @@ const kernelRebuildPeriod = syzkallerRebuildPeriod + time.Hour
|
|||||||
|
|
||||||
// List of required files in kernel build (contents of latest/current dirs).
|
// List of required files in kernel build (contents of latest/current dirs).
|
||||||
var imageFiles = []string{
|
var imageFiles = []string{
|
||||||
"kernel.tag", // git hash of kernel checkout
|
"tag", // serialized BuildInfo
|
||||||
"compiler.tag", // compiler identity string (e.g. "gcc 7.1.1")
|
"kernel.config", // kernel config used for build
|
||||||
"kernel.config", // kernel config used for the build (identified with SHA1 hash of contents)
|
|
||||||
"tag", // SHA1 hash of the previous 3 tags (this is what uniquely identifies the build)
|
|
||||||
"image", // kernel image
|
"image", // kernel image
|
||||||
"key", // root ssh key for the image
|
"key", // root ssh key for the image
|
||||||
"obj/vmlinux", // vmlinux with debug info
|
"obj/vmlinux", // vmlinux with debug info
|
||||||
@ -45,18 +43,18 @@ var imageFiles = []string{
|
|||||||
// - latest: latest known good kernel build
|
// - latest: latest known good kernel build
|
||||||
// - current: currently used kernel build
|
// - current: currently used kernel build
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
name string
|
name string
|
||||||
workDir string
|
workDir string
|
||||||
kernelDir string
|
kernelDir string
|
||||||
currentDir string
|
currentDir string
|
||||||
latestDir string
|
latestDir string
|
||||||
compilerTag string
|
compilerID string
|
||||||
configTag string
|
configTag string
|
||||||
cfg *Config
|
cfg *Config
|
||||||
mgrcfg *ManagerConfig
|
mgrcfg *ManagerConfig
|
||||||
cmd *ManagerCmd
|
cmd *ManagerCmd
|
||||||
dash *dashboard.Dashboard
|
dash *dashboard.Dashboard
|
||||||
stop chan struct{}
|
stop chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createManager(dash *dashboard.Dashboard, cfg *Config, mgrcfg *ManagerConfig, stop chan struct{}) *Manager {
|
func createManager(dash *dashboard.Dashboard, cfg *Config, mgrcfg *ManagerConfig, stop chan struct{}) *Manager {
|
||||||
@ -66,7 +64,7 @@ func createManager(dash *dashboard.Dashboard, cfg *Config, mgrcfg *ManagerConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assume compiler and config don't change underneath us.
|
// Assume compiler and config don't change underneath us.
|
||||||
compilerTag, err := kernel.CompilerIdentity(mgrcfg.Compiler)
|
compilerID, err := kernel.CompilerIdentity(mgrcfg.Compiler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatal(err)
|
Fatal(err)
|
||||||
}
|
}
|
||||||
@ -76,17 +74,17 @@ func createManager(dash *dashboard.Dashboard, cfg *Config, mgrcfg *ManagerConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
mgr := &Manager{
|
mgr := &Manager{
|
||||||
name: mgrcfg.Name,
|
name: mgrcfg.Name,
|
||||||
workDir: filepath.Join(dir, "workdir"),
|
workDir: filepath.Join(dir, "workdir"),
|
||||||
kernelDir: filepath.Join(dir, "kernel"),
|
kernelDir: filepath.Join(dir, "kernel"),
|
||||||
currentDir: filepath.Join(dir, "current"),
|
currentDir: filepath.Join(dir, "current"),
|
||||||
latestDir: filepath.Join(dir, "latest"),
|
latestDir: filepath.Join(dir, "latest"),
|
||||||
compilerTag: compilerTag,
|
compilerID: compilerID,
|
||||||
configTag: hash.String(configData),
|
configTag: hash.String(configData),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
mgrcfg: mgrcfg,
|
mgrcfg: mgrcfg,
|
||||||
dash: dash,
|
dash: dash,
|
||||||
stop: stop,
|
stop: stop,
|
||||||
}
|
}
|
||||||
os.RemoveAll(mgr.currentDir)
|
os.RemoveAll(mgr.currentDir)
|
||||||
return mgr
|
return mgr
|
||||||
@ -101,19 +99,19 @@ func (mgr *Manager) loop() {
|
|||||||
lastCommit := ""
|
lastCommit := ""
|
||||||
nextBuildTime := time.Now()
|
nextBuildTime := time.Now()
|
||||||
var managerRestartTime time.Time
|
var managerRestartTime time.Time
|
||||||
latestTime, latestKernelTag, latestCompilerTag, latestConfigTag := mgr.checkLatest()
|
latestInfo := mgr.checkLatest()
|
||||||
if time.Since(latestTime) < kernelRebuildPeriod/2 {
|
if latestInfo != nil && time.Since(latestInfo.Time) < kernelRebuildPeriod/2 {
|
||||||
// If we have a reasonably fresh build,
|
// If we have a reasonably fresh build,
|
||||||
// start manager straight away and don't rebuild kernel for a while.
|
// start manager straight away and don't rebuild kernel for a while.
|
||||||
Logf(0, "%v: using latest image built on %v", mgr.name, latestKernelTag)
|
Logf(0, "%v: using latest image built on %v", mgr.name, latestInfo.KernelCommit)
|
||||||
managerRestartTime = latestTime
|
managerRestartTime = latestInfo.Time
|
||||||
nextBuildTime = time.Now().Add(kernelRebuildPeriod)
|
nextBuildTime = time.Now().Add(kernelRebuildPeriod)
|
||||||
mgr.restartManager()
|
mgr.restartManager()
|
||||||
} else {
|
} else if latestInfo != nil {
|
||||||
Logf(0, "%v: latest image is on %v", mgr.name, formatTag(latestKernelTag))
|
Logf(0, "%v: latest image is on %v", mgr.name, latestInfo.KernelCommit)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(kernelRebuildPeriod)
|
ticker := time.NewTicker(buildRetryPeriod)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
@ -126,9 +124,10 @@ loop:
|
|||||||
} else {
|
} else {
|
||||||
Logf(0, "%v: poll: %v", mgr.name, commit)
|
Logf(0, "%v: poll: %v", mgr.name, commit)
|
||||||
if commit != lastCommit &&
|
if commit != lastCommit &&
|
||||||
(commit != latestKernelTag ||
|
(latestInfo == nil ||
|
||||||
mgr.compilerTag != latestCompilerTag ||
|
commit != latestInfo.KernelCommit ||
|
||||||
mgr.configTag != latestConfigTag) {
|
mgr.compilerID != latestInfo.CompilerID ||
|
||||||
|
mgr.configTag != latestInfo.KernelConfigTag) {
|
||||||
lastCommit = commit
|
lastCommit = commit
|
||||||
select {
|
select {
|
||||||
case kernelBuildSem <- struct{}{}:
|
case kernelBuildSem <- struct{}{}:
|
||||||
@ -138,7 +137,10 @@ loop:
|
|||||||
} else {
|
} else {
|
||||||
Logf(0, "%v: build successful, [re]starting manager", mgr.name)
|
Logf(0, "%v: build successful, [re]starting manager", mgr.name)
|
||||||
rebuildAfter = kernelRebuildPeriod
|
rebuildAfter = kernelRebuildPeriod
|
||||||
latestTime, latestKernelTag, latestCompilerTag, latestConfigTag = mgr.checkLatest()
|
latestInfo = mgr.checkLatest()
|
||||||
|
if latestInfo == nil {
|
||||||
|
Logf(0, "%v: failed to read build info after build", mgr.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<-kernelBuildSem
|
<-kernelBuildSem
|
||||||
case <-mgr.stop:
|
case <-mgr.stop:
|
||||||
@ -155,8 +157,8 @@ loop:
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if managerRestartTime != latestTime {
|
if latestInfo != nil && (latestInfo.Time != managerRestartTime || mgr.cmd == nil) {
|
||||||
managerRestartTime = latestTime
|
managerRestartTime = latestInfo.Time
|
||||||
mgr.restartManager()
|
mgr.restartManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,24 +176,33 @@ loop:
|
|||||||
Logf(0, "%v: stopped", mgr.name)
|
Logf(0, "%v: stopped", mgr.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkLatest checks if we have a good working latest build
|
// BuildInfo characterizes a kernel build.
|
||||||
// and returns its kernel/compiler/config tags.
|
type BuildInfo struct {
|
||||||
// If the build is missing/broken, zero mod time is returned.
|
Time time.Time // when the build was done
|
||||||
func (mgr *Manager) checkLatest() (mod time.Time, kernelTag, compilerTag, configTag string) {
|
Tag string // unique tag combined from compiler id, kernel commit and config tag
|
||||||
|
CompilerID string // compiler identity string (e.g. "gcc 7.1.1")
|
||||||
|
KernelRepo string
|
||||||
|
KernelBranch string
|
||||||
|
KernelCommit string // git hash of kernel checkout
|
||||||
|
KernelConfigTag string // SHA1 hash of .config contents
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBuildInfo(dir string) (*BuildInfo, error) {
|
||||||
|
info := new(BuildInfo)
|
||||||
|
if err := config.LoadFile(filepath.Join(dir, "tag"), info); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkLatest checks if we have a good working latest build and returns its build info.
|
||||||
|
// If the build is missing/broken, nil is returned.
|
||||||
|
func (mgr *Manager) checkLatest() *BuildInfo {
|
||||||
if !osutil.FilesExist(mgr.latestDir, imageFiles) {
|
if !osutil.FilesExist(mgr.latestDir, imageFiles) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
configData, err := ioutil.ReadFile(filepath.Join(mgr.latestDir, "kernel.config"))
|
info, _ := loadBuildInfo(mgr.latestDir)
|
||||||
if err != nil {
|
return info
|
||||||
return
|
|
||||||
}
|
|
||||||
configTag = hash.String(configData)
|
|
||||||
compilerTag, _ = readTag(filepath.Join(mgr.latestDir, "compiler.tag"))
|
|
||||||
if compilerTag == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
kernelTag, mod = readTag(filepath.Join(mgr.latestDir, "kernel.tag"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgr *Manager) build() error {
|
func (mgr *Manager) build() error {
|
||||||
@ -230,26 +241,21 @@ func (mgr *Manager) build() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTagFile := func(filename, data string) error {
|
var tagData []byte
|
||||||
f := filepath.Join(tmpDir, filename)
|
tagData = append(tagData, kernelCommit...)
|
||||||
if err := ioutil.WriteFile(f, []byte(data), osutil.DefaultFilePerm); err != nil {
|
tagData = append(tagData, mgr.compilerID...)
|
||||||
return fmt.Errorf("failed to write tag file: %v", err)
|
tagData = append(tagData, mgr.configTag...)
|
||||||
}
|
info := &BuildInfo{
|
||||||
return nil
|
Time: time.Now(),
|
||||||
|
Tag: hash.String(tagData),
|
||||||
|
CompilerID: mgr.compilerID,
|
||||||
|
KernelRepo: mgr.mgrcfg.Repo,
|
||||||
|
KernelBranch: mgr.mgrcfg.Branch,
|
||||||
|
KernelCommit: kernelCommit,
|
||||||
|
KernelConfigTag: mgr.configTag,
|
||||||
}
|
}
|
||||||
if err := writeTagFile("kernel.tag", kernelCommit); err != nil {
|
if err := config.SaveFile(filepath.Join(tmpDir, "tag"), info); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to write tag file: %v", err)
|
||||||
}
|
|
||||||
if err := writeTagFile("compiler.tag", mgr.compilerTag); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var tag []byte
|
|
||||||
tag = append(tag, kernelCommit...)
|
|
||||||
tag = append(tag, mgr.configTag...)
|
|
||||||
tag = append(tag, mgr.compilerTag...)
|
|
||||||
if err := writeTagFile("tag", hash.String(tag)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now try to replace latest with our tmp dir as atomically as we can get on Linux.
|
// Now try to replace latest with our tmp dir as atomically as we can get on Linux.
|
||||||
@ -272,7 +278,12 @@ func (mgr *Manager) restartManager() {
|
|||||||
Logf(0, "%v: failed to create current image dir: %v", mgr.name, err)
|
Logf(0, "%v: failed to create current image dir: %v", mgr.name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfgFile, err := mgr.writeConfig()
|
info, err := loadBuildInfo(mgr.currentDir)
|
||||||
|
if err != nil {
|
||||||
|
Logf(0, "%v: failed to load build info: %v", mgr.name, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfgFile, err := mgr.writeConfig(info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logf(0, "%v: failed to create manager config: %v", mgr.name, err)
|
Logf(0, "%v: failed to create manager config: %v", mgr.name, err)
|
||||||
return
|
return
|
||||||
@ -282,12 +293,12 @@ func (mgr *Manager) restartManager() {
|
|||||||
mgr.cmd = NewManagerCmd(mgr.name, logFile, bin, "-config", cfgFile)
|
mgr.cmd = NewManagerCmd(mgr.name, logFile, bin, "-config", cfgFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgr *Manager) writeConfig() (string, error) {
|
func (mgr *Manager) writeConfig(info *BuildInfo) (string, error) {
|
||||||
mgrcfg := &mgrconfig.Config{
|
mgrcfg := &mgrconfig.Config{
|
||||||
Cover: true,
|
Cover: true,
|
||||||
Reproduce: true,
|
Reproduce: true,
|
||||||
Sandbox: "setuid",
|
Sandbox: "setuid",
|
||||||
Rpc: "localhost:0",
|
Rpc: ":0",
|
||||||
Procs: 1,
|
Procs: 1,
|
||||||
}
|
}
|
||||||
err := config.LoadData(mgr.mgrcfg.Manager_Config, mgrcfg)
|
err := config.LoadData(mgr.mgrcfg.Manager_Config, mgrcfg)
|
||||||
@ -295,11 +306,14 @@ func (mgr *Manager) writeConfig() (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
current := mgr.currentDir
|
current := mgr.currentDir
|
||||||
// TODO(dvyukov): we use kernel.tag because dashboard does not support build info yet.
|
tag := info.Tag
|
||||||
// Later we should use tag file because it identifies kernel+compiler+config.
|
if mgr.dash == nil {
|
||||||
tag, err := ioutil.ReadFile(filepath.Join(current, "kernel.tag"))
|
// Dashboard identifies builds by unique tags that are combined
|
||||||
if err != nil {
|
// from kernel tag, compiler tag and config tag.
|
||||||
return "", fmt.Errorf("failed to read tag file: %v", err)
|
// This combined tag is meaningless without dashboard,
|
||||||
|
// so we use kenrel tag (commit tag) because it communicates
|
||||||
|
// at least some useful information.
|
||||||
|
tag = info.KernelCommit
|
||||||
}
|
}
|
||||||
mgrcfg.Name = mgr.cfg.Name + "-" + mgr.name
|
mgrcfg.Name = mgr.cfg.Name + "-" + mgr.name
|
||||||
mgrcfg.Hub_Addr = mgr.cfg.Hub_Addr
|
mgrcfg.Hub_Addr = mgr.cfg.Hub_Addr
|
||||||
@ -308,7 +322,11 @@ func (mgr *Manager) writeConfig() (string, error) {
|
|||||||
mgrcfg.Dashboard_Key = mgr.cfg.Dashboard_Key
|
mgrcfg.Dashboard_Key = mgr.cfg.Dashboard_Key
|
||||||
mgrcfg.Workdir = mgr.workDir
|
mgrcfg.Workdir = mgr.workDir
|
||||||
mgrcfg.Vmlinux = filepath.Join(current, "obj", "vmlinux")
|
mgrcfg.Vmlinux = filepath.Join(current, "obj", "vmlinux")
|
||||||
mgrcfg.Tag = string(tag)
|
// Strictly saying this is somewhat racy as builder can concurrently
|
||||||
|
// update the source, or even delete and re-clone. If this causes
|
||||||
|
// problems, we need to make a copy of sources after build.
|
||||||
|
mgrcfg.Kernel_Src = mgr.kernelDir
|
||||||
|
mgrcfg.Tag = tag
|
||||||
mgrcfg.Syzkaller = filepath.FromSlash("syzkaller/current")
|
mgrcfg.Syzkaller = filepath.FromSlash("syzkaller/current")
|
||||||
mgrcfg.Image = filepath.Join(current, "image")
|
mgrcfg.Image = filepath.Join(current, "image")
|
||||||
mgrcfg.Sshkey = filepath.Join(current, "key")
|
mgrcfg.Sshkey = filepath.Join(current, "key")
|
||||||
|
@ -95,8 +95,12 @@ func (upd *SyzUpdater) UpdateOnStart(shutdown chan struct{}) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Logf(0, "current executable is on %v", formatTag(exeTag))
|
if exeTag == "" {
|
||||||
Logf(0, "latest syzkaller build is on %v", formatTag(latestTag))
|
Logf(0, "current executable is bootstrap")
|
||||||
|
} else {
|
||||||
|
Logf(0, "current executable is on %v", exeTag)
|
||||||
|
Logf(0, "latest syzkaller build is on %v", latestTag)
|
||||||
|
}
|
||||||
|
|
||||||
// No syzkaller build or executable is stale.
|
// No syzkaller build or executable is stale.
|
||||||
lastCommit := ""
|
lastCommit := ""
|
||||||
@ -220,10 +224,3 @@ func readTag(file string) (tag string, mod time.Time) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatTag(tag string) string {
|
|
||||||
if tag == "" {
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
return tag
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user