Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kata-sys-util: Move CPU info functions
Move get_single_cpu_info and get_cpu_flags into kata-sys-util. Add new functions that get a list of flags and check if a flag exists in that list. Fixes #6383 Signed-off-by: Nathan Whyte <nathanwhyte35@gmail.com> Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
- Loading branch information
1 parent
52100bb
commit 304b9d9
Showing
3 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,372 @@ | ||
// Copyright (c) 2022 Intel Corporation | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
|
||
use anyhow::{anyhow, Result}; | ||
|
||
#[allow(dead_code)] | ||
const ERR_NO_CPUINFO: &str = "cpu_info string is empty"; | ||
|
||
pub const PROC_CPUINFO: &str = "/proc/cpuinfo"; | ||
|
||
#[cfg(target_arch = "x86_64")] | ||
pub const CPUINFO_DELIMITER: &str = "\nprocessor"; | ||
#[cfg(target_arch = "x86_64")] | ||
pub const CPUINFO_FLAGS_TAG: &str = "flags"; | ||
|
||
fn read_file_contents(file_path: &str) -> Result<String> { | ||
let contents = std::fs::read_to_string(file_path)?; | ||
Ok(contents) | ||
} | ||
|
||
// get_single_cpu_info returns the contents of the first cpu from | ||
// the specified cpuinfo file by parsing based on a specified delimiter | ||
pub fn get_single_cpu_info(cpu_info_file: &str, substring: &str) -> Result<String> { | ||
let contents = read_file_contents(cpu_info_file)?; | ||
|
||
if contents.is_empty() { | ||
return Err(anyhow!(ERR_NO_CPUINFO)); | ||
} | ||
|
||
let subcontents: Vec<&str> = contents.split(substring).collect(); | ||
let result = subcontents | ||
.first() | ||
.ok_or("error splitting contents of cpuinfo") | ||
.map_err(|e| anyhow!(e))? | ||
.to_string(); | ||
Ok(result) | ||
} | ||
|
||
// get_cpu_flags returns a string of cpu flags from cpuinfo, passed in | ||
// as a string | ||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] | ||
pub fn get_cpu_flags(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> { | ||
if cpu_info.is_empty() { | ||
return Err(anyhow!(ERR_NO_CPUINFO)); | ||
} | ||
|
||
if cpu_flags_tag.is_empty() { | ||
return Err(anyhow!("cpu flags delimiter string is empty"))?; | ||
} | ||
|
||
get_cpu_flags_from_file(cpu_info, cpu_flags_tag) | ||
} | ||
|
||
// get a list of cpu flags in cpu_info_flags | ||
// | ||
// cpu_info is the content of cpuinfo file passed in as a string | ||
// returns empty Vec if no flags are found | ||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] | ||
pub fn get_cpu_flags_vec(cpu_info: &str, cpu_flags_tag: &str) -> Result<Vec<String>> { | ||
if cpu_info.is_empty() { | ||
return Err(anyhow!(ERR_NO_CPUINFO)); | ||
} | ||
|
||
if cpu_flags_tag.is_empty() { | ||
return Err(anyhow!("cpu flags delimiter string is empty"))?; | ||
} | ||
|
||
let flags = get_cpu_flags_from_file(cpu_info, cpu_flags_tag)?; | ||
|
||
// take each flag, trim whitespace, convert to String, and add to list | ||
// skip the first token in the iterator since it is empty | ||
let flags_vec: Vec<String> = flags | ||
.split(' ') | ||
.skip(1) | ||
.map(|f| f.trim().to_string()) | ||
.collect::<Vec<String>>(); | ||
|
||
Ok(flags_vec) | ||
} | ||
|
||
// check if the given flag exists in the given flags_vec | ||
// | ||
// flags_vec can be created by calling get_cpu_flags_vec | ||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] | ||
pub fn contains_cpu_flag(flags_vec: &[String], flag: &str) -> Result<bool> { | ||
if flag.is_empty() { | ||
return Err(anyhow!("parameter specifying flag to look for is empty"))?; | ||
} | ||
|
||
Ok(flags_vec.iter().any(|f| f == flag)) | ||
} | ||
|
||
// get a String containing the cpu flags in cpu_info | ||
// | ||
// this function returns the list of flags as a single String | ||
// if no flags are found, returns an empty String | ||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] | ||
fn get_cpu_flags_from_file(cpu_info: &str, cpu_flags_tag: &str) -> Result<String> { | ||
let subcontents: Vec<&str> = cpu_info.split('\n').collect(); | ||
for line in subcontents { | ||
if line.starts_with(cpu_flags_tag) { | ||
let line_data: Vec<&str> = line.split(':').collect(); | ||
let flags = line_data | ||
.last() | ||
.ok_or("error splitting flags in cpuinfo") | ||
.map_err(|e| anyhow!(e))? | ||
.to_string(); | ||
return Ok(flags); | ||
} | ||
} | ||
|
||
Ok("".to_string()) | ||
} | ||
|
||
#[cfg(any(target_arch = "s390x", target_arch = "x86_64"))] | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use std::fs; | ||
use std::io::Write; | ||
use tempfile::tempdir; | ||
|
||
#[test] | ||
fn test_get_single_cpu_info() { | ||
// Valid cpuinfo example | ||
let dir = tempdir().unwrap(); | ||
let file_path_full = dir.path().join("cpuinfo_full"); | ||
let path_full = file_path_full.clone(); | ||
let mut file_full = fs::File::create(file_path_full).unwrap(); | ||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n".to_string(); | ||
writeln!(file_full, "{}", contents).unwrap(); | ||
|
||
// Empty cpuinfo example | ||
let file_path_empty = dir.path().join("cpuinfo_empty"); | ||
let path_empty = file_path_empty.clone(); | ||
let mut _file_empty = fs::File::create(file_path_empty).unwrap(); | ||
|
||
#[derive(Debug)] | ||
struct TestData<'a> { | ||
cpuinfo_path: &'a str, | ||
processor_delimiter_str: &'a str, | ||
result: Result<String>, | ||
} | ||
let tests = &[ | ||
// Failure scenarios | ||
TestData { | ||
cpuinfo_path: "", | ||
processor_delimiter_str: "", | ||
result: Err(anyhow!("No such file or directory (os error 2)")), | ||
}, | ||
TestData { | ||
cpuinfo_path: &path_empty.as_path().display().to_string(), | ||
processor_delimiter_str: "\nprocessor", | ||
result: Err(anyhow!(ERR_NO_CPUINFO)), | ||
}, | ||
// Success scenarios | ||
TestData { | ||
cpuinfo_path: &path_full.as_path().display().to_string(), | ||
processor_delimiter_str: "\nprocessor", | ||
result: Ok( | ||
"processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4" | ||
.to_string(), | ||
), | ||
}, | ||
]; | ||
|
||
for (i, d) in tests.iter().enumerate() { | ||
let msg = format!("test[{}]: {:?}", i, d); | ||
let result = get_single_cpu_info(d.cpuinfo_path, d.processor_delimiter_str); | ||
let msg = format!("{}, result: {:?}", msg, result); | ||
|
||
if d.result.is_ok() { | ||
assert_eq!( | ||
result.as_ref().unwrap(), | ||
d.result.as_ref().unwrap(), | ||
"{}", | ||
msg | ||
); | ||
continue; | ||
} | ||
|
||
let expected_error = format!("{}", d.result.as_ref().unwrap_err()); | ||
let actual_error = format!("{}", result.unwrap_err()); | ||
assert!(actual_error == expected_error, "{}", msg); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_get_cpu_flags() { | ||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n"; | ||
|
||
#[derive(Debug)] | ||
struct TestData<'a> { | ||
cpu_info_str: &'a str, | ||
cpu_flags_tag: &'a str, | ||
result: Result<String>, | ||
} | ||
let tests = &[ | ||
// Failure scenarios | ||
TestData { | ||
cpu_info_str: "", | ||
cpu_flags_tag: "", | ||
result: Err(anyhow!(ERR_NO_CPUINFO)), | ||
}, | ||
TestData { | ||
cpu_info_str: "", | ||
cpu_flags_tag: "flags", | ||
result: Err(anyhow!(ERR_NO_CPUINFO)), | ||
}, | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "", | ||
result: Err(anyhow!("cpu flags delimiter string is empty")), | ||
}, | ||
// Success scenarios | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "flags", | ||
result: Ok(" flag_1 flag_2 flag_3 flag_4".to_string()), | ||
}, | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "flags_err", | ||
result: Ok("".to_string()), | ||
}, | ||
]; | ||
|
||
for (i, d) in tests.iter().enumerate() { | ||
let msg = format!("test[{}]: {:?}", i, d); | ||
let result = get_cpu_flags(d.cpu_info_str, d.cpu_flags_tag); | ||
let msg = format!("{}, result: {:?}", msg, result); | ||
|
||
if d.result.is_ok() { | ||
assert_eq!( | ||
result.as_ref().unwrap(), | ||
d.result.as_ref().unwrap(), | ||
"{}", | ||
msg | ||
); | ||
continue; | ||
} | ||
|
||
let expected_error = format!("{}", d.result.as_ref().unwrap_err()); | ||
let actual_error = format!("{}", result.unwrap_err()); | ||
assert!(actual_error == expected_error, "{}", msg); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_get_cpu_flags_vec() { | ||
let contents = "processor : 0\nvendor_id : VendorExample\nflags : flag_1 flag_2 flag_3 flag_4\nprocessor : 1\n"; | ||
|
||
#[derive(Debug)] | ||
struct TestData<'a> { | ||
cpu_info_str: &'a str, | ||
cpu_flags_tag: &'a str, | ||
result: Result<Vec<String>>, | ||
} | ||
let tests = &[ | ||
// Failure scenarios | ||
TestData { | ||
cpu_info_str: "", | ||
cpu_flags_tag: "", | ||
result: Err(anyhow!(ERR_NO_CPUINFO)), | ||
}, | ||
TestData { | ||
cpu_info_str: "", | ||
cpu_flags_tag: "flags", | ||
result: Err(anyhow!(ERR_NO_CPUINFO)), | ||
}, | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "", | ||
result: Err(anyhow!("cpu flags delimiter string is empty")), | ||
}, | ||
// Success scenarios | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "flags", | ||
result: Ok(vec![ | ||
"flag_1".to_string(), | ||
"flag_2".to_string(), | ||
"flag_3".to_string(), | ||
"flag_4".to_string(), | ||
]), | ||
}, | ||
TestData { | ||
cpu_info_str: contents, | ||
cpu_flags_tag: "flags_err", | ||
result: Ok(Vec::new()), | ||
}, | ||
]; | ||
|
||
for (i, d) in tests.iter().enumerate() { | ||
let msg = format!("test[{}]: {:?}", i, d); | ||
let result = get_cpu_flags_vec(d.cpu_info_str, d.cpu_flags_tag); | ||
let msg = format!("{}, result: {:?}", msg, result); | ||
|
||
if d.result.is_ok() { | ||
assert_eq!( | ||
result.as_ref().unwrap(), | ||
d.result.as_ref().unwrap(), | ||
"{}", | ||
msg | ||
); | ||
continue; | ||
} | ||
|
||
let expected_error = format!("{}", d.result.as_ref().unwrap_err()); | ||
let actual_error = format!("{}", result.unwrap_err()); | ||
assert!(actual_error == expected_error, "{}", msg); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_contains_cpu_flag() { | ||
let flags_vec = vec![ | ||
"flag_1".to_string(), | ||
"flag_2".to_string(), | ||
"flag_3".to_string(), | ||
"flag_4".to_string(), | ||
]; | ||
|
||
#[derive(Debug)] | ||
struct TestData<'a> { | ||
cpu_flags_vec: &'a Vec<String>, | ||
cpu_flag: &'a str, | ||
result: Result<bool>, | ||
} | ||
let tests = &[ | ||
// Failure scenarios | ||
TestData { | ||
cpu_flags_vec: &flags_vec, | ||
cpu_flag: "flag_5", | ||
result: Ok(false), | ||
}, | ||
TestData { | ||
cpu_flags_vec: &flags_vec, | ||
cpu_flag: "", | ||
result: Err(anyhow!("parameter specifying flag to look for is empty")), | ||
}, | ||
// Success scenarios | ||
TestData { | ||
cpu_flags_vec: &flags_vec, | ||
cpu_flag: "flag_1", | ||
result: Ok(true), | ||
}, | ||
]; | ||
|
||
for (i, d) in tests.iter().enumerate() { | ||
let msg = format!("test[{}]: {:?}", i, d); | ||
let result = contains_cpu_flag(&d.cpu_flags_vec, d.cpu_flag); | ||
let msg = format!("{}, result: {:?}", msg, result); | ||
|
||
if d.result.is_ok() { | ||
assert_eq!( | ||
result.as_ref().unwrap(), | ||
d.result.as_ref().unwrap(), | ||
"{}", | ||
msg | ||
); | ||
continue; | ||
} | ||
|
||
let expected_error = format!("{}", d.result.as_ref().unwrap_err()); | ||
let actual_error = format!("{}", result.unwrap_err()); | ||
assert!(actual_error == expected_error, "{}", msg); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
#[macro_use] | ||
extern crate slog; | ||
|
||
pub mod cpu; | ||
pub mod device; | ||
pub mod fs; | ||
pub mod hooks; | ||
|