summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-07-14 18:15:35 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2024-07-14 18:58:01 -0400
commitdbae42c7300313f6062ebfe41b24f90c0b75ae9e (patch)
tree3e127936853ebaefd809cf782a3fd73f8e376677
parent349da300a45e742c304e0ed51b78f046469fadcd (diff)
get-test-job now uses subtest duration
Some tests run much quicker than others - but when we give out test jobs, we want to give out a similar amount of work each time - say 5 minutes. This uses the table generated by gen-avg-duration to give out work up to the amount defined by the config file. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--src/bin/gen-avg-duration.rs4
-rw-r--r--src/bin/gen-job-list.rs2
-rw-r--r--src/bin/get-test-job.rs66
-rw-r--r--src/lib.rs16
4 files changed, 69 insertions, 19 deletions
diff --git a/src/bin/gen-avg-duration.rs b/src/bin/gen-avg-duration.rs
index 2968895..8deaa57 100644
--- a/src/bin/gen-avg-duration.rs
+++ b/src/bin/gen-avg-duration.rs
@@ -67,7 +67,9 @@ fn write_durations_capnp(rc: &Ktestrc, durations_in: TestDurationMap) {
serialize::write_message(&mut out, &message).unwrap();
drop(out);
- std::fs::rename(fname_new, fname).unwrap();
+ std::fs::rename(fname_new, &fname).unwrap();
+
+ println!("wrote durations for {} tests to {}", durations_in.len(), fname.display());
}
fn main() {
diff --git a/src/bin/gen-job-list.rs b/src/bin/gen-job-list.rs
index c03109f..c41fc29 100644
--- a/src/bin/gen-job-list.rs
+++ b/src/bin/gen-job-list.rs
@@ -118,7 +118,7 @@ fn branch_test_jobs(rc: &CiConfig, repo: &git2::Repository,
let missing_subtests: Vec<_> = subtests
.iter()
.filter(|i| {
- let full_subtest_name = subtest_full_name(&test_path, &i);
+ let full_subtest_name = subtest_full_name(&test_path.to_string_lossy(), &i);
!have_result(&results, &full_subtest_name) &&
!lockfile_exists(&rc.ktest, &commit, &full_subtest_name,
diff --git a/src/bin/get-test-job.rs b/src/bin/get-test-job.rs
index 5d48a95..ff12edc 100644
--- a/src/bin/get-test-job.rs
+++ b/src/bin/get-test-job.rs
@@ -1,6 +1,6 @@
extern crate libc;
use std::collections::HashSet;
-use std::path::Path;
+use std::fs::File;
use std::process;
use ci_cgi::{Ktestrc, ciconfig_read, lockfile_exists, commit_update_results_from_fs, subtest_full_name};
use ci_cgi::{Worker, workers_update};
@@ -34,6 +34,9 @@ use memmap::MmapOptions;
use std::fs::OpenOptions;
use std::str;
+use ci_cgi::durations_capnp::durations;
+use capnp::serialize;
+
fn commit_test_matches(job: &Option<TestJob>, commit: &str, test: &str) -> bool {
if let Some(job) = job {
if job.commit == commit && job.test == test {
@@ -44,7 +47,35 @@ fn commit_test_matches(job: &Option<TestJob>, commit: &str, test: &str) -> bool
false
}
-fn get_test_job(rc: &Ktestrc) -> Option<TestJob> {
+fn test_duration(durations: &Option<durations::Reader>, test: &str, subtest: &str) -> Option<u64> {
+ use std::cmp::Ordering::*;
+
+ if let Some(d) = durations.as_ref() {
+ let full_test = subtest_full_name(test, subtest);
+ let full_test = full_test.as_str();
+
+ let d = d.get_entries().unwrap();
+
+ let mut l = 0;
+ let mut r = d.len();
+
+ while l < r {
+ let m = l + (r - l) / 2;
+ let d_m = d.get(m);
+ let d_m_test = d_m.get_test().unwrap().to_str().unwrap();
+
+ match full_test.cmp(d_m_test) {
+ Less => r = m,
+ Equal => return Some(d_m.get_duration()),
+ Greater => l = m,
+ }
+ }
+ }
+
+ None
+}
+
+fn get_test_job(args: &Args, rc: &Ktestrc, durations: &Option<durations::Reader>) -> Option<TestJob> {
let file = OpenOptions::new()
.read(true)
.write(true)
@@ -54,6 +85,8 @@ fn get_test_job(rc: &Ktestrc) -> Option<TestJob> {
return None;
}
+ let mut duration_sum: u64 = 0;
+
let map = unsafe { MmapOptions::new().map(&file).unwrap() };
let mut ret = None;
@@ -82,12 +115,20 @@ fn get_test_job(rc: &Ktestrc) -> Option<TestJob> {
len = job.as_ptr() as u64 - map.as_ptr() as u64;
} else if commit_test_matches(&ret, commit, test) {
if let Some(ref mut r) = ret {
- r.subtests.push(subtest.to_string());
- len = job.as_ptr() as u64 - map.as_ptr() as u64;
+ let duration_secs = test_duration(durations, test, subtest);
+
+ if args.verbose {
+ println!("duration for {}.{}={:?}", test, subtest, duration_secs);
+ }
+
+ let duration_secs = duration_secs.unwrap_or(rc.subtest_duration_def);
- if r.subtests.len() > 20 {
+ if duration_sum != 0 && duration_sum + duration_secs > rc.subtest_duration_max {
break;
}
+
+ duration_sum += duration_secs;
+ r.subtests.push(subtest.to_string());
}
} else {
break;
@@ -104,7 +145,7 @@ fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option<TestJob> {
job.subtests = job.subtests.iter()
.filter(|i| lockfile_exists(rc, &job.commit,
- &subtest_full_name(&Path::new(&job.test), &i),
+ &subtest_full_name(&job.test, &i),
true,
&mut commits_updated))
.map(|i| i.to_string())
@@ -117,9 +158,9 @@ fn create_job_lockfiles(rc: &Ktestrc, mut job: TestJob) -> Option<TestJob> {
if !job.subtests.is_empty() { Some(job) } else { None }
}
-fn get_and_lock_job(rc: &Ktestrc) -> Option<TestJob> {
+fn get_and_lock_job(args: &Args, rc: &Ktestrc, durations: &Option<durations::Reader>) -> Option<TestJob> {
loop {
- let job = get_test_job(rc);
+ let job = get_test_job(args, rc, durations);
if let Some(job) = job {
let job = create_job_lockfiles(rc, job);
if job.is_some() {
@@ -143,13 +184,18 @@ fn main() {
let rc = rc.unwrap();
let rc = rc.ktest;
+ let durations_file = File::open(rc.output_dir.join("test_durations.capnp")).ok();
+ let durations_map = durations_file.map(|x| unsafe { MmapOptions::new().map(&x).ok() } ).flatten();
+ let durations_reader = durations_map.as_ref().map(|x| serialize::read_message_from_flat_slice(&mut &x[..], capnp::message::ReaderOptions::new()).ok()).flatten();
+ let durations = durations_reader.as_ref().map(|x| x.get_root::<durations::Reader>().ok()).flatten();
+
let lockfile = rc.output_dir.join("jobs.lock");
let filelock = FileLock::lock(lockfile, true, FileOptions::new().create(true).write(true)).unwrap();
let job = if !args.dry_run {
- get_and_lock_job(&rc)
+ get_and_lock_job(&args, &rc, &durations)
} else {
- get_test_job(&rc)
+ get_test_job(&args, &rc, &durations)
};
drop(filelock);
diff --git a/src/lib.rs b/src/lib.rs
index a503c52..5d66f2f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashSet};
use std::fs::{File, OpenOptions, create_dir_all, read_to_string};
use std::io::ErrorKind;
use std::io::prelude::*;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::time::SystemTime;
use die::die;
use serde_derive::Deserialize;
@@ -33,10 +33,12 @@ pub fn git_get_commit(repo: &git2::Repository, reference: String) -> Result<git2
#[derive(Deserialize)]
pub struct Ktestrc {
- pub linux_repo: PathBuf,
- pub output_dir: PathBuf,
- pub ktest_dir: PathBuf,
- pub users_dir: PathBuf,
+ pub linux_repo: PathBuf,
+ pub output_dir: PathBuf,
+ pub ktest_dir: PathBuf,
+ pub users_dir: PathBuf,
+ pub subtest_duration_max: u64,
+ pub subtest_duration_def: u64,
}
pub fn ktestrc_read() -> anyhow::Result<Ktestrc> {
@@ -362,8 +364,8 @@ pub fn update_lcov(rc: &Ktestrc, commit_id: &String) -> Option<()> {
Some(())
}
-pub fn subtest_full_name(test: &Path, subtest: &String) -> String {
- let test = test.to_string_lossy();
+pub fn subtest_full_name(test: &str, subtest: &str) -> String {
+ let test = test.to_owned();
let test = test.replace(".ktest", "");
let test = test + "." + subtest;
let test = test.replace("/", ".");