1use std::fmt::Write;
6use std::path::Path;
7
8use crate::context::Context;
9use crate::libtorch::detect;
10use crate::util::system;
11
12pub fn run(json: bool) {
13 let ctx = Context::resolve();
14 let root = &ctx.root;
15 if json {
16 print_json(root, &ctx);
17 } else {
18 print_report(root, &ctx);
19 }
20}
21
22fn print_report(root: &Path, ctx: &Context) {
27 println!("floDl Diagnostics");
28 println!("=================");
29 println!();
30
31 println!("Context: {}", ctx.label());
33 println!();
34
35 println!("System");
37 let cpu = system::cpu_model().unwrap_or_else(|| "Unknown".into());
38 let threads = system::cpu_threads();
39 let ram_gb = system::ram_total_gb();
40 println!(
41 " CPU: {} ({} threads, {}GB RAM)",
42 cpu, threads, ram_gb
43 );
44 if let Some(os) = system::os_version() {
45 println!(" OS: {}", os);
46 }
47 if system::is_inside_docker() {
48 println!(" Docker: yes (running inside container)");
49 } else {
50 match system::docker_version() {
51 Some(v) => println!(" Docker: {}", v),
52 None => println!(" Docker: not found"),
53 }
54 }
55 println!();
56
57 println!("GPU");
65 let sweep = flodl_hw::survey();
66 let devices = &sweep.devices;
67 if !devices.is_empty() {
68 if sweep.has_vendor(system::GpuVendor::Nvidia)
69 && let Some(driver) = system::nvidia_driver_version()
70 {
71 println!(" NVIDIA driver: {}", driver);
72 }
73 println!(" Devices: {}", devices.len());
74 for d in devices {
75 let vram_gb = d.total_memory_mb / 1024;
76 println!(
77 " [{}] {} -- {}, {}, {}GB VRAM",
78 d.index,
79 d.name,
80 d.vendor,
81 d.arch_label(),
82 vram_gb
83 );
84 }
85 } else {
86 println!(" No GPU devices available");
87 }
88 for note in &sweep.notes {
89 println!(" Note: {}", note);
90 }
91 println!();
92
93 println!("libtorch");
95 match detect::read_active(root) {
96 Some(info) => {
97 println!(" Active: {}", info.path);
98 if let Some(v) = &info.torch_version {
99 println!(" Version: {}", v);
100 }
101 match detect::variant_vendor(&info.path) {
107 Some(v) => println!(" Vendor: {}", v),
108 None => println!(" Vendor: CPU-only"),
109 }
110 if let Some(c) = info.cuda_version.as_deref().filter(|c| *c != "none") {
111 println!(" CUDA: {}", c);
112 }
113 if let Some(a) = &info.archs {
114 println!(" Archs: {}", a);
115 }
116 if let Some(s) = &info.source {
117 println!(" Source: {}", s);
118 }
119 }
120 None => {
121 println!(" No active variant (run `fdl setup`)");
122 }
123 }
124
125 let variants = detect::list_variants(root);
126 if !variants.is_empty() {
127 println!(" Variants: {}", variants.join(", "));
128 }
129 println!();
130
131 if !devices.is_empty() {
133 println!("Compatibility");
134 if let Some(info) = detect::read_active(root) {
135 let archs = info.archs.as_deref().unwrap_or("");
136 let mut all_ok = true;
137 for d in devices {
138 if d.covered_by(archs) {
139 println!(
140 " GPU {} ({}, {}): OK",
141 d.index,
142 d.short_name(),
143 d.arch_label()
144 );
145 } else {
146 all_ok = false;
147 println!(
148 " GPU {} ({}, {}): MISSING -- arch {} not in [{}]",
149 d.index,
150 d.short_name(),
151 d.arch_label(),
152 d.arch.archs_token(),
155 archs
156 );
157 }
158 }
159 if all_ok {
160 println!();
161 println!(" All GPUs compatible with active libtorch.");
162 }
163 } else {
164 println!(" Cannot check -- no active libtorch variant.");
165 }
166 println!();
167 }
168}
169
170fn print_json(root: &Path, ctx: &Context) {
175 let mut b = String::with_capacity(2048);
176 b.push('{');
177
178 let _ = write!(
180 b,
181 "\"context\":{{\"mode\":\"{}\",\"root\":\"{}\"}}",
182 if ctx.is_project { "project" } else { "global" },
183 system::escape_json(&ctx.root.display().to_string())
184 );
185
186 let cpu = system::cpu_model().unwrap_or_else(|| "Unknown".into());
188 let _ = write!(
189 b,
190 ",\"system\":{{\"cpu\":\"{}\",\"threads\":{},\"ram_gb\":{}",
191 system::escape_json(&cpu),
192 system::cpu_threads(),
193 system::ram_total_gb()
194 );
195 if let Some(os) = system::os_version() {
196 let _ = write!(b, ",\"os\":\"{}\"", system::escape_json(&os));
197 }
198 if system::is_inside_docker() {
199 b.push_str(",\"docker\":\"container\"");
200 } else if let Some(docker) = system::docker_version() {
201 let _ = write!(b, ",\"docker\":\"{}\"", system::escape_json(&docker));
202 }
203 b.push('}');
204
205 let sweep = flodl_hw::survey();
210 let devices = &sweep.devices;
211 let archs = detect::read_active(root)
212 .and_then(|info| info.archs)
213 .unwrap_or_default();
214 b.push_str(",\"gpus\":[");
215 for (i, d) in devices.iter().enumerate() {
216 if i > 0 {
217 b.push(',');
218 }
219 let compatible = d.covered_by(&archs);
220 let _ = write!(
224 b,
225 "{{\"index\":{},\"name\":\"{}\",\"vendor\":\"{}\",\"arch\":\"{}\",\"sm\":\"{}\",\"vram_bytes\":{},\"arch_compatible\":{}}}",
226 d.index,
227 system::escape_json(&d.name),
228 d.vendor.as_str(),
229 d.arch_label(),
230 d.sm_version().unwrap_or_default(),
231 d.vram_bytes(),
232 compatible
233 );
234 }
235 b.push(']');
236
237 b.push_str(",\"gpu_notes\":[");
240 for (i, note) in sweep.notes.iter().enumerate() {
241 if i > 0 {
242 b.push(',');
243 }
244 let _ = write!(
245 b,
246 "{{\"vendor\":\"{}\",\"kind\":\"{}\",\"message\":\"{}\"}}",
247 note.vendor.as_str(),
248 note.kind.as_str(),
249 system::escape_json(¬e.message),
250 );
251 }
252 b.push(']');
253
254 b.push_str(",\"libtorch\":");
256 match detect::read_active(root) {
257 Some(info) => {
258 let _ = write!(b, "{{\"path\":\"{}\"", system::escape_json(&info.path));
259 if let Some(v) = &info.torch_version {
260 let _ = write!(b, ",\"version\":\"{}\"", system::escape_json(v));
261 }
262 let _ = write!(
267 b,
268 ",\"vendor\":\"{}\"",
269 match detect::variant_vendor(&info.path) {
270 Some(v) => v.as_str(),
271 None => "cpu",
272 }
273 );
274 if let Some(c) = &info.cuda_version {
275 let _ = write!(b, ",\"cuda\":\"{}\"", system::escape_json(c));
276 }
277 if let Some(a) = &info.archs {
278 let _ = write!(b, ",\"archs\":\"{}\"", system::escape_json(a));
279 }
280 if let Some(s) = &info.source {
281 let _ = write!(b, ",\"source\":\"{}\"", system::escape_json(s));
282 }
283 b.push('}');
284 }
285 None => b.push_str("null"),
286 }
287
288 b.push('}');
289 println!("{}", b);
290}