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}