Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions arch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,32 @@ edition.workspace = true
name = "arch"
version = "0.1.0"

# TODO: Consider making this a binary of the main package instead
[[bin]]
name = "generate-cpu-profile"
path = "src/bin/generate-cpu-profile.rs"
required-features = ["cpu_profile_generation"]

[features]
default = []
fw_cfg = []
kvm = ["hypervisor/kvm"]
sev_snp = []
tdx = []
# Currently cpu profiles can only be generated with KVM
cpu_profile_generation = ["kvm", "dep:clap"]

[dependencies]
anyhow = { workspace = true }
byteorder = { workspace = true }
clap = { workspace = true, optional = true }
hypervisor = { path = "../hypervisor" }
libc = { workspace = true }
linux-loader = { workspace = true, features = ["bzimage", "elf", "pe"] }
log = { workspace = true }
serde = { workspace = true, features = ["derive", "rc"] }
# We currently use this for (de-)serializing CPU profile data
serde_json = { workspace = true }
thiserror = { workspace = true }
uuid = { workspace = true }
vm-memory = { workspace = true, features = ["backend-bitmap", "backend-mmap"] }
Expand All @@ -28,5 +39,10 @@ vmm-sys-util = { workspace = true, features = ["with-serde"] }
fdt_parser = { version = "0.1.5", package = "fdt" }
vm-fdt = { workspace = true }

# Use this to test our custom serialization logic
[dev-dependencies]
proptest = "1.0.0"
serde_json = { workspace = true }

[lints]
workspace = true
34 changes: 34 additions & 0 deletions arch/src/bin/generate-cpu-profile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![cfg(all(
target_arch = "x86_64",
feature = "cpu_profile_generation",
feature = "kvm"
))]
use std::io::BufWriter;

use anyhow::Context;
use clap::{Arg, Command};

fn main() -> anyhow::Result<()> {
let cmd_arg = Command::new("generate-cpu-profile")
.version(env!("CARGO_PKG_VERSION"))
.arg_required_else_help(true)
.arg(
Arg::new("name")
.help("The name to give the CPU profile")
.num_args(1)
.required(true),
)
.get_matches();

let profile_name = cmd_arg.get_one::<String>("name").unwrap();

let hypervisor = hypervisor::new().context("Could not obtain hypervisor")?;
// TODO: Consider letting the user provide a file path as a target instead of writing to stdout.
// The way it is now should be sufficient for a PoC however.
let writer = BufWriter::new(std::io::stdout().lock());
arch::x86_64::cpu_profile_generation::generate_profile_data(
writer,
hypervisor.as_ref(),
profile_name,
)
}
30 changes: 30 additions & 0 deletions arch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
extern crate log;

use std::collections::BTreeMap;
use std::str::FromStr;
use std::sync::Arc;
use std::{fmt, result};

use serde::de::IntoDeserializer;
use serde::{Deserialize, Serialize};
use thiserror::Error;

#[cfg(target_arch = "x86_64")]
pub use crate::x86_64::cpu_profile::CpuProfile;

type GuestMemoryMmap = vm_memory::GuestMemoryMmap<vm_memory::bitmap::AtomicBitmap>;
type GuestRegionMmap = vm_memory::GuestRegionMmap<vm_memory::bitmap::AtomicBitmap>;

Expand Down Expand Up @@ -56,6 +61,31 @@ pub enum Error {
/// Type for returning public functions outcome.
pub type Result<T> = result::Result<T, Error>;

// If the target_arch is x86_64 we import CpuProfile from the x86_64 module, otherwise we
// declare it here.
#[cfg(not(target_arch = "x86_64"))]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
/// A [`CpuProfile`] is a mechanism for ensuring live migration compatibility
/// between host's with potentially different CPU models.
pub enum CpuProfile {
#[default]
Host,
}

impl FromStr for CpuProfile {
type Err = serde::de::value::Error;
fn from_str(s: &str) -> result::Result<Self, Self::Err> {
// Should accept both plain strings, and strings surrounded by `"`.
let normalized = s
.strip_prefix('"')
.unwrap_or(s)
.strip_suffix('"')
.unwrap_or(s);
Self::deserialize(normalized.into_deserializer())
}
}

/// Type for memory region types.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum RegionType {
Expand Down
Loading
Loading