#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InstallMethod {
Global,
Venv,
Auto,
}
impl std::fmt::Display for InstallMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
InstallMethod::Global => write!(f, "global"),
InstallMethod::Venv => write!(f, "venv"),
InstallMethod::Auto => write!(f, "auto"),
}
}
}
#[derive(Debug, Clone)]
pub struct PytorchProfilerConfig {
pub python_bin: String,
pub method: InstallMethod,
pub dry_run: bool,
pub strict_rocm: bool,
}
impl Default for PytorchProfilerConfig {
fn default() -> Self {
Self {
python_bin: "python3".to_string(),
method: InstallMethod::Auto,
dry_run: false,
strict_rocm: true,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShellCommand {
pub program: String,
pub args: Vec<String>,
pub env: Vec<(String, String)>,
}
impl ShellCommand {
pub fn to_command_string(&self) -> String {
let env_prefix = self
.env
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(" ");
let cmd = if self.args.is_empty() {
self.program.clone()
} else {
format!("{} {}", self.program, self.args.join(" "))
};
if env_prefix.is_empty() {
cmd
} else {
format!("{env_prefix} {cmd}")
}
}
}
pub struct PytorchProfilerInstaller {
config: PytorchProfilerConfig,
}
impl PytorchProfilerInstaller {
pub fn new(config: PytorchProfilerConfig) -> Self {
Self { config }
}
pub fn with_defaults() -> Self {
Self::new(PytorchProfilerConfig::default())
}
pub fn dependencies(&self) -> &[&str] {
&["pytorch"]
}
pub fn validate_dependencies(&self, installed_components: &[&str]) -> anyhow::Result<()> {
for dep in self.dependencies() {
if !installed_components.contains(dep) {
anyhow::bail!("PyTorch Profiler requires '{}' to be installed first", dep);
}
}
Ok(())
}
pub fn build_install_command(&self) -> ShellCommand {
let use_break = self.config.method == InstallMethod::Global
|| self.config.method == InstallMethod::Auto;
let mut args = vec!["-m".to_string(), "pip".to_string(), "install".to_string()];
if use_break {
args.push("--break-system-packages".to_string());
}
args.extend(["torch-tb-profiler".to_string(), "tensorboard".to_string()]);
ShellCommand {
program: self.config.python_bin.clone(),
args,
env: vec![],
}
}
pub fn build_rocm_env(&self) -> Vec<(String, String)> {
vec![
("ROCM_PATH".to_string(), "/opt/rocm".to_string()),
(
"PATH".to_string(),
"/opt/rocm/bin:".to_string() + &std::env::var("PATH").unwrap_or_default(),
),
(
"LD_LIBRARY_PATH".to_string(),
"/opt/rocm/lib:".to_string()
+ &std::env::var("LD_LIBRARY_PATH").unwrap_or_default(),
),
("HSA_OVERRIDE_GFX_VERSION".to_string(), "11.0.0".to_string()),
("PYTORCH_ROCM_ARCH".to_string(), "gfx1100".to_string()),
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_install_command_global() {
let installer = PytorchProfilerInstaller::new(PytorchProfilerConfig {
method: InstallMethod::Global,
..Default::default()
});
let cmd = installer.build_install_command();
assert_eq!(cmd.program, "python3");
assert!(cmd.args.contains(&"-m".to_string()));
assert!(cmd.args.contains(&"pip".to_string()));
assert!(cmd.args.contains(&"install".to_string()));
assert!(cmd.args.contains(&"--break-system-packages".to_string()));
assert!(cmd.args.contains(&"torch-tb-profiler".to_string()));
assert!(cmd.args.contains(&"tensorboard".to_string()));
}
#[test]
fn test_install_command_venv() {
let installer = PytorchProfilerInstaller::new(PytorchProfilerConfig {
method: InstallMethod::Venv,
..Default::default()
});
let cmd = installer.build_install_command();
assert!(!cmd.args.contains(&"--break-system-packages".to_string()));
assert!(cmd.args.contains(&"torch-tb-profiler".to_string()));
assert!(cmd.args.contains(&"tensorboard".to_string()));
}
#[test]
fn test_dependencies() {
let installer = PytorchProfilerInstaller::with_defaults();
assert!(installer.dependencies().contains(&"pytorch"));
}
#[test]
fn test_validate_dependencies_success() {
let installer = PytorchProfilerInstaller::with_defaults();
assert!(installer.validate_dependencies(&["pytorch"]).is_ok());
}
#[test]
fn test_validate_dependencies_missing() {
let installer = PytorchProfilerInstaller::with_defaults();
let result = installer.validate_dependencies(&[]);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("pytorch"));
}
#[test]
fn test_rocm_env() {
let installer = PytorchProfilerInstaller::with_defaults();
let env = installer.build_rocm_env();
assert!(env
.iter()
.any(|(k, v)| k == "ROCM_PATH" && v == "/opt/rocm"));
assert!(env
.iter()
.any(|(k, v)| k == "HSA_OVERRIDE_GFX_VERSION" && v == "11.0.0"));
}
}