compiletest/
debuggers.rs
1use std::env;
2use std::ffi::OsString;
3use std::path::{Path, PathBuf};
4use std::process::Command;
5use std::sync::Arc;
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 env::set_var("RUST_TEST_THREADS", "1");
44 }
45
46 Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
47}
48
49pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
50 config.lldb_python_dir.as_ref()?;
51
52 if let Some(350) = config.lldb_version {
53 println!(
54 "WARNING: The used version of LLDB (350) has a \
55 known issue that breaks debuginfo tests. See \
56 issue #32520 for more information. Skipping all \
57 LLDB-based tests!",
58 );
59 return None;
60 }
61
62 Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
63}
64
65pub(crate) fn is_android_gdb_target(target: &str) -> bool {
68 matches!(
69 &target[..],
70 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
71 )
72}
73
74fn is_pc_windows_msvc_target(target: &str) -> bool {
76 target.ends_with("-pc-windows-msvc")
77}
78
79fn find_cdb(target: &str) -> Option<OsString> {
80 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
81 return None;
82 }
83
84 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
85 let cdb_arch = if cfg!(target_arch = "x86") {
86 "x86"
87 } else if cfg!(target_arch = "x86_64") {
88 "x64"
89 } else if cfg!(target_arch = "aarch64") {
90 "arm64"
91 } else if cfg!(target_arch = "arm") {
92 "arm"
93 } else {
94 return None; };
96
97 let mut path = PathBuf::new();
98 path.push(pf86);
99 path.push(r"Windows Kits\10\Debuggers"); path.push(cdb_arch);
101 path.push(r"cdb.exe");
102
103 if !path.exists() {
104 return None;
105 }
106
107 Some(path.into_os_string())
108}
109
110pub(crate) fn analyze_cdb(
112 cdb: Option<String>,
113 target: &str,
114) -> (Option<OsString>, Option<[u16; 4]>) {
115 let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
116
117 let mut version = None;
118 if let Some(cdb) = cdb.as_ref() {
119 if let Ok(output) = Command::new(cdb).arg("/version").output() {
120 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
121 version = extract_cdb_version(&first_line);
122 }
123 }
124 }
125
126 (cdb, version)
127}
128
129pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
130 let version = full_version_line.rsplit(' ').next()?;
132 let mut components = version.split('.');
133 let major: u16 = components.next().unwrap().parse().unwrap();
134 let minor: u16 = components.next().unwrap().parse().unwrap();
135 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
136 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
137 Some([major, minor, patch, build])
138}
139
140pub(crate) fn analyze_gdb(
142 gdb: Option<String>,
143 target: &str,
144 android_cross_path: &Path,
145) -> (Option<String>, Option<u32>) {
146 #[cfg(not(windows))]
147 const GDB_FALLBACK: &str = "gdb";
148 #[cfg(windows)]
149 const GDB_FALLBACK: &str = "gdb.exe";
150
151 let fallback_gdb = || {
152 if is_android_gdb_target(target) {
153 let mut gdb_path = match android_cross_path.to_str() {
154 Some(x) => x.to_owned(),
155 None => panic!("cannot find android cross path"),
156 };
157 gdb_path.push_str("/bin/gdb");
158 gdb_path
159 } else {
160 GDB_FALLBACK.to_owned()
161 }
162 };
163
164 let gdb = match gdb {
165 None => fallback_gdb(),
166 Some(ref s) if s.is_empty() => fallback_gdb(), Some(ref s) => s.to_owned(),
168 };
169
170 let mut version_line = None;
171 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
172 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
173 version_line = Some(first_line.to_string());
174 }
175 }
176
177 let version = match version_line {
178 Some(line) => extract_gdb_version(&line),
179 None => return (None, None),
180 };
181
182 (Some(gdb), version)
183}
184
185pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
186 let full_version_line = full_version_line.trim();
187
188 let unbracketed_part = full_version_line.split('[').next().unwrap();
200 let mut splits = unbracketed_part.trim_end().rsplit(' ');
201 let version_string = splits.next().unwrap();
202
203 let mut splits = version_string.split('.');
204 let major = splits.next().unwrap();
205 let minor = splits.next().unwrap();
206 let patch = splits.next();
207
208 let major: u32 = major.parse().unwrap();
209 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
210 None => {
211 let minor = minor.parse().unwrap();
212 let patch: u32 = match patch {
213 Some(patch) => match patch.find(not_a_digit) {
214 None => patch.parse().unwrap(),
215 Some(idx) if idx > 3 => 0,
216 Some(idx) => patch[..idx].parse().unwrap(),
217 },
218 None => 0,
219 };
220 (minor, patch)
221 }
222 Some(idx) => {
224 let minor = minor[..idx].parse().unwrap();
225 (minor, 0)
226 }
227 };
228
229 Some(((major * 1000) + minor) * 1000 + patch)
230}
231
232pub(crate) fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
234 let full_version_line = full_version_line.trim();
253
254 if let Some(apple_ver) =
255 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
256 {
257 if let Some(idx) = apple_ver.find(not_a_digit) {
258 let version: u32 = apple_ver[..idx].parse().unwrap();
259 return Some(version);
260 }
261 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
262 if let Some(idx) = lldb_ver.find(not_a_digit) {
263 let version: u32 = lldb_ver[..idx].parse().ok()?;
264 return Some(version * 100);
265 }
266 }
267 None
268}
269
270fn not_a_digit(c: char) -> bool {
271 !c.is_ascii_digit()
272}