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    bundle_type: Option<String>,
 19    gpu_specs: Option<String>,
 20}
 21
 22impl SystemSpecs {
 23    pub fn new(window: &mut Window, cx: &mut App) -> Task<Self> {
 24        let app_version = AppVersion::global(cx).to_string();
 25        let release_channel = ReleaseChannel::global(cx);
 26        let os_name = telemetry::os_name();
 27        let system = System::new_with_specifics(
 28            RefreshKind::new().with_memory(MemoryRefreshKind::everything()),
 29        );
 30        let memory = system.total_memory();
 31        let architecture = env::consts::ARCH;
 32        let commit_sha = match release_channel {
 33            ReleaseChannel::Dev | ReleaseChannel::Nightly => {
 34                AppCommitSha::try_global(cx).map(|sha| sha.full().clone())
 35            }
 36            _ => None,
 37        };
 38        let bundle_type = bundle_type();
 39
 40        let gpu_specs = window.gpu_specs().map(|specs| {
 41            format!(
 42                "{} || {} || {}",
 43                specs.device_name, specs.driver_name, specs.driver_info
 44            )
 45        });
 46
 47        cx.background_spawn(async move {
 48            let os_version = telemetry::os_version();
 49            SystemSpecs {
 50                app_version,
 51                release_channel: release_channel.display_name(),
 52                bundle_type,
 53                os_name,
 54                os_version,
 55                memory,
 56                architecture,
 57                commit_sha,
 58                gpu_specs,
 59            }
 60        })
 61    }
 62
 63    pub fn new_stateless(
 64        app_version: SemanticVersion,
 65        app_commit_sha: Option<AppCommitSha>,
 66        release_channel: ReleaseChannel,
 67    ) -> Self {
 68        let os_name = telemetry::os_name();
 69        let os_version = telemetry::os_version();
 70        let system = System::new_with_specifics(
 71            RefreshKind::new().with_memory(MemoryRefreshKind::everything()),
 72        );
 73        let memory = system.total_memory();
 74        let architecture = env::consts::ARCH;
 75        let commit_sha = match release_channel {
 76            ReleaseChannel::Dev | ReleaseChannel::Nightly => app_commit_sha.map(|sha| sha.full()),
 77            _ => None,
 78        };
 79        let bundle_type = bundle_type();
 80
 81        Self {
 82            app_version: app_version.to_string(),
 83            release_channel: release_channel.display_name(),
 84            os_name,
 85            os_version,
 86            memory,
 87            architecture,
 88            commit_sha,
 89            bundle_type,
 90            gpu_specs: try_determine_available_gpus(),
 91        }
 92    }
 93}
 94
 95impl Display for SystemSpecs {
 96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 97        let os_information = format!("OS: {} {}", self.os_name, self.os_version);
 98        let app_version_information = format!(
 99            "Zed: v{} ({}) {}{}",
100            self.app_version,
101            match &self.commit_sha {
102                Some(commit_sha) => format!("{} {}", self.release_channel, commit_sha),
103                None => self.release_channel.to_string(),
104            },
105            if let Some(bundle_type) = &self.bundle_type {
106                format!("({bundle_type})")
107            } else {
108                "".to_string()
109            },
110            if cfg!(debug_assertions) {
111                "(Taylor's Version)"
112            } else {
113                ""
114            },
115        );
116        let system_specs = [
117            app_version_information,
118            os_information,
119            format!("Memory: {}", human_bytes(self.memory as f64)),
120            format!("Architecture: {}", self.architecture),
121        ]
122        .into_iter()
123        .chain(
124            self.gpu_specs
125                .as_ref()
126                .map(|specs| format!("GPU: {}", specs)),
127        )
128        .collect::<Vec<String>>()
129        .join("\n");
130
131        write!(f, "{system_specs}")
132    }
133}
134
135fn try_determine_available_gpus() -> Option<String> {
136    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
137    {
138        std::process::Command::new("vulkaninfo")
139            .args(&["--summary"])
140            .output()
141            .ok()
142            .map(|output| {
143                [
144                    "<details><summary>`vulkaninfo --summary` output</summary>",
145                    "",
146                    "```",
147                    String::from_utf8_lossy(&output.stdout).as_ref(),
148                    "```",
149                    "</details>",
150                ]
151                .join("\n")
152            })
153            .or(Some("Failed to run `vulkaninfo --summary`".to_string()))
154    }
155    #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
156    {
157        None
158    }
159}
160
161/// Returns value of `ZED_BUNDLE_TYPE` set at compiletime or else at runtime.
162///
163/// The compiletime value is used by flatpak since it doesn't seem to have a way to provide a
164/// runtime environment variable.
165///
166/// The runtime value is used by snap since the Zed snaps use release binaries directly, and so
167/// cannot have this baked in.
168fn bundle_type() -> Option<String> {
169    option_env!("ZED_BUNDLE_TYPE")
170        .map(|bundle_type| bundle_type.to_string())
171        .or_else(|| env::var("ZED_BUNDLE_TYPE").ok())
172}