1use std::env;
2use std::process::Command;
3use std::sync::Arc;
4
5use camino::{Utf8Path, Utf8PathBuf};
6
7use crate::common::{Config, Debugger};
8
9pub(crate) fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
10 config.cdb.as_ref()?;
11
12 Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() }))
13}
14
15pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
16 config.gdb_version?;
17
18 if config.matches_env("msvc") {
19 return None;
20 }
21
22 if config.remote_test_client.is_some() && !config.target.contains("android") {
23 println!(
24 "WARNING: debuginfo tests are not available when \
25 testing with remote"
26 );
27 return None;
28 }
29
30 if config.target.contains("android") {
31 println!(
32 "{} debug-info test uses tcp 5039 port.\
33 please reserve it",
34 config.target
35 );
36
37 unsafe { env::set_var("RUST_TEST_THREADS", "1") };
46 }
47
48 Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
49}
50
51pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
52 config.lldb_python_dir.as_ref()?;
53
54 if let Some(350) = config.lldb_version {
56 println!(
57 "WARNING: The used version of LLDB (350) has a \
58 known issue that breaks debuginfo tests. See \
59 issue #32520 for more information. Skipping all \
60 LLDB-based tests!",
61 );
62 return None;
63 }
64
65 Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
66}
67
68pub(crate) fn is_android_gdb_target(target: &str) -> bool {
71 matches!(
72 &target[..],
73 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
74 )
75}
76
77fn is_pc_windows_msvc_target(target: &str) -> bool {
79 target.ends_with("-pc-windows-msvc")
80}
81
82fn find_cdb(target: &str) -> Option<Utf8PathBuf> {
84 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
85 return None;
86 }
87
88 let pf86 = Utf8PathBuf::from_path_buf(
89 env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?.into(),
90 )
91 .unwrap();
92 let cdb_arch = if cfg!(target_arch = "x86") {
93 "x86"
94 } else if cfg!(target_arch = "x86_64") {
95 "x64"
96 } else if cfg!(target_arch = "aarch64") {
97 "arm64"
98 } else if cfg!(target_arch = "arm") {
99 "arm"
100 } else {
101 return None; };
103
104 let mut path = pf86;
105 path.push(r"Windows Kits\10\Debuggers"); path.push(cdb_arch);
107 path.push(r"cdb.exe");
108
109 if !path.exists() {
110 return None;
111 }
112
113 Some(path)
114}
115
116pub(crate) fn analyze_cdb(
118 cdb: Option<String>,
119 target: &str,
120) -> (Option<Utf8PathBuf>, Option<[u16; 4]>) {
121 let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target));
122
123 let mut version = None;
124 if let Some(cdb) = cdb.as_ref() {
125 if let Ok(output) = Command::new(cdb).arg("/version").output() {
126 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
127 version = extract_cdb_version(&first_line);
128 }
129 }
130 }
131
132 (cdb, version)
133}
134
135pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
136 let version = full_version_line.rsplit(' ').next()?;
138 let mut components = version.split('.');
139 let major: u16 = components.next().unwrap().parse().unwrap();
140 let minor: u16 = components.next().unwrap().parse().unwrap();
141 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
142 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
143 Some([major, minor, patch, build])
144}
145
146pub(crate) fn analyze_gdb(
148 gdb: Option<String>,
149 target: &str,
150 android_cross_path: &Utf8Path,
151) -> (Option<String>, Option<u32>) {
152 #[cfg(not(windows))]
153 const GDB_FALLBACK: &str = "gdb";
154 #[cfg(windows)]
155 const GDB_FALLBACK: &str = "gdb.exe";
156
157 let fallback_gdb = || {
158 if is_android_gdb_target(target) {
159 let mut gdb_path = android_cross_path.to_string();
160 gdb_path.push_str("/bin/gdb");
161 gdb_path
162 } else {
163 GDB_FALLBACK.to_owned()
164 }
165 };
166
167 let gdb = match gdb {
168 None => fallback_gdb(),
169 Some(ref s) if s.is_empty() => fallback_gdb(), Some(ref s) => s.to_owned(),
171 };
172
173 let mut version_line = None;
174 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
175 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
176 version_line = Some(first_line.to_string());
177 }
178 }
179
180 let version = match version_line {
181 Some(line) => extract_gdb_version(&line),
182 None => return (None, None),
183 };
184
185 (Some(gdb), version)
186}
187
188pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
189 let full_version_line = full_version_line.trim();
190
191 let unbracketed_part = full_version_line.split('[').next().unwrap();
203 let mut splits = unbracketed_part.trim_end().rsplit(' ');
204 let version_string = splits.next().unwrap();
205
206 let mut splits = version_string.split('.');
207 let major = splits.next().unwrap();
208 let minor = splits.next().unwrap();
209 let patch = splits.next();
210
211 let major: u32 = major.parse().unwrap();
212 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
213 None => {
214 let minor = minor.parse().unwrap();
215 let patch: u32 = match patch {
216 Some(patch) => match patch.find(not_a_digit) {
217 None => patch.parse().unwrap(),
218 Some(idx) if idx > 3 => 0,
219 Some(idx) => patch[..idx].parse().unwrap(),
220 },
221 None => 0,
222 };
223 (minor, patch)
224 }
225 Some(idx) => {
227 let minor = minor[..idx].parse().unwrap();
228 (minor, 0)
229 }
230 };
231
232 Some(((major * 1000) + minor) * 1000 + patch)
233}
234
235pub(crate) fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
237 let full_version_line = full_version_line.trim();
256
257 if let Some(apple_ver) =
258 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
259 {
260 if let Some(idx) = apple_ver.find(not_a_digit) {
261 let version: u32 = apple_ver[..idx].parse().unwrap();
262 return Some(version);
263 }
264 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
265 if let Some(idx) = lldb_ver.find(not_a_digit) {
266 let version: u32 = lldb_ver[..idx].parse().ok()?;
267 return Some(version * 100);
268 }
269 }
270 None
271}
272
273fn not_a_digit(c: char) -> bool {
274 !c.is_ascii_digit()
275}