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.0.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 => {
 74                app_commit_sha.map(|sha| sha.0.clone())
 75            }
 76            _ => None,
 77        };
 78
 79        Self {
 80            app_version: app_version.to_string(),
 81            release_channel: release_channel.display_name(),
 82            os_name,
 83            os_version,
 84            memory,
 85            architecture,
 86            commit_sha,
 87            gpu_specs: try_determine_available_gpus(),
 88        }
 89    }
 90}
 91
 92impl Display for SystemSpecs {
 93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 94        let os_information = format!("OS: {} {}", self.os_name, self.os_version);
 95        let app_version_information = format!(
 96            "Zed: v{} ({}) {}",
 97            self.app_version,
 98            match &self.commit_sha {
 99                Some(commit_sha) => format!("{} {}", self.release_channel, commit_sha),
100                None => self.release_channel.to_string(),
101            },
102            if cfg!(debug_assertions) {
103                "(Taylor's Version)"
104            } else {
105                ""
106            },
107        );
108        let system_specs = [
109            app_version_information,
110            os_information,
111            format!("Memory: {}", human_bytes(self.memory as f64)),
112            format!("Architecture: {}", self.architecture),
113        ]
114        .into_iter()
115        .chain(
116            self.gpu_specs
117                .as_ref()
118                .map(|specs| format!("GPU: {}", specs)),
119        )
120        .collect::<Vec<String>>()
121        .join("\n");
122
123        write!(f, "{system_specs}")
124    }
125}
126
127fn try_determine_available_gpus() -> Option<String> {
128    #[cfg(target_os = "linux")]
129    {
130        return std::process::Command::new("vulkaninfo")
131            .args(&["--summary"])
132            .output()
133            .ok()
134            .map(|output| {
135                [
136                    "<details><summary>`vulkaninfo --summary` output</summary>",
137                    "",
138                    "```",
139                    String::from_utf8_lossy(&output.stdout).as_ref(),
140                    "```",
141                    "</details>",
142                ]
143                .join("\n")
144            })
145            .or(Some("Failed to run `vulkaninfo --summary`".to_string()));
146    }
147    #[cfg(not(target_os = "linux"))]
148    {
149        return None;
150    }
151}