system_specs.rs

  1use client::telemetry;
  2use gpui::{App, AppContext as _, SemanticVersion, Task, Window};
  3use human_bytes::human_bytes;
  4use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
  5use serde::Serialize;
  6use std::{env, fmt::Display};
  7use sysinfo::{MemoryRefreshKind, RefreshKind, System};
  8
  9#[derive(Clone, Debug, Serialize)]
 10pub struct SystemSpecs {
 11    app_version: String,
 12    release_channel: &'static str,
 13    os_name: String,
 14    os_version: String,
 15    memory: u64,
 16    architecture: &'static str,
 17    commit_sha: Option<String>,
 18    gpu_specs: Option<String>,
 19}
 20
 21impl SystemSpecs {
 22    pub fn new(window: &mut Window, cx: &mut App) -> Task<Self> {
 23        let app_version = AppVersion::global(cx).to_string();
 24        let release_channel = ReleaseChannel::global(cx);
 25        let os_name = telemetry::os_name();
 26        let system = System::new_with_specifics(
 27            RefreshKind::new().with_memory(MemoryRefreshKind::everything()),
 28        );
 29        let memory = system.total_memory();
 30        let architecture = env::consts::ARCH;
 31        let commit_sha = match release_channel {
 32            ReleaseChannel::Dev | ReleaseChannel::Nightly => {
 33                AppCommitSha::try_global(cx).map(|sha| sha.full().clone())
 34            }
 35            _ => None,
 36        };
 37
 38        let gpu_specs = window.gpu_specs().map(|specs| {
 39            format!(
 40                "{} || {} || {}",
 41                specs.device_name, specs.driver_name, specs.driver_info
 42            )
 43        });
 44
 45        cx.background_spawn(async move {
 46            let os_version = telemetry::os_version();
 47            SystemSpecs {
 48                app_version,
 49                release_channel: release_channel.display_name(),
 50                os_name,
 51                os_version,
 52                memory,
 53                architecture,
 54                commit_sha,
 55                gpu_specs,
 56            }
 57        })
 58    }
 59
 60    pub fn new_stateless(
 61        app_version: SemanticVersion,
 62        app_commit_sha: Option<AppCommitSha>,
 63        release_channel: ReleaseChannel,
 64    ) -> Self {
 65        let os_name = telemetry::os_name();
 66        let os_version = telemetry::os_version();
 67        let system = System::new_with_specifics(
 68            RefreshKind::new().with_memory(MemoryRefreshKind::everything()),
 69        );
 70        let memory = system.total_memory();
 71        let architecture = env::consts::ARCH;
 72        let commit_sha = match release_channel {
 73            ReleaseChannel::Dev | ReleaseChannel::Nightly => app_commit_sha.map(|sha| sha.full()),
 74            _ => None,
 75        };
 76
 77        Self {
 78            app_version: app_version.to_string(),
 79            release_channel: release_channel.display_name(),
 80            os_name,
 81            os_version,
 82            memory,
 83            architecture,
 84            commit_sha,
 85            gpu_specs: try_determine_available_gpus(),
 86        }
 87    }
 88}
 89
 90impl Display for SystemSpecs {
 91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 92        let os_information = format!("OS: {} {}", self.os_name, self.os_version);
 93        let app_version_information = format!(
 94            "Zed: v{} ({}) {}",
 95            self.app_version,
 96            match &self.commit_sha {
 97                Some(commit_sha) => format!("{} {}", self.release_channel, commit_sha),
 98                None => self.release_channel.to_string(),
 99            },
100            if cfg!(debug_assertions) {
101                "(Taylor's Version)"
102            } else {
103                ""
104            },
105        );
106        let system_specs = [
107            app_version_information,
108            os_information,
109            format!("Memory: {}", human_bytes(self.memory as f64)),
110            format!("Architecture: {}", self.architecture),
111        ]
112        .into_iter()
113        .chain(
114            self.gpu_specs
115                .as_ref()
116                .map(|specs| format!("GPU: {}", specs)),
117        )
118        .collect::<Vec<String>>()
119        .join("\n");
120
121        write!(f, "{system_specs}")
122    }
123}
124
125fn try_determine_available_gpus() -> Option<String> {
126    #[cfg(target_os = "linux")]
127    {
128        return std::process::Command::new("vulkaninfo")
129            .args(&["--summary"])
130            .output()
131            .ok()
132            .map(|output| {
133                [
134                    "<details><summary>`vulkaninfo --summary` output</summary>",
135                    "",
136                    "```",
137                    String::from_utf8_lossy(&output.stdout).as_ref(),
138                    "```",
139                    "</details>",
140                ]
141                .join("\n")
142            })
143            .or(Some("Failed to run `vulkaninfo --summary`".to_string()));
144    }
145    #[cfg(not(target_os = "linux"))]
146    {
147        return None;
148    }
149}