1use std::ffi::{OsStr, OsString};
2use std::fs::File;
3use std::io::{BufRead, BufReader, Read};
4use std::process::{Command, Output, Stdio};
5
6use camino::Utf8Path;
7use tracing::debug;
8
9use super::debugger::DebuggerCommands;
10use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
11use crate::common::Config;
12use crate::debuggers::{extract_gdb_version, is_android_gdb_target};
13
14impl TestCx<'_> {
15 pub(super) fn run_debuginfo_test(&self) {
16 match self.config.debugger.unwrap() {
17 Debugger::Cdb => self.run_debuginfo_cdb_test(),
18 Debugger::Gdb => self.run_debuginfo_gdb_test(),
19 Debugger::Lldb => self.run_debuginfo_lldb_test(),
20 }
21 }
22
23 fn run_debuginfo_cdb_test(&self) {
24 let config = Config {
25 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
26 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
27 ..self.config.clone()
28 };
29
30 let test_cx = TestCx { config: &config, ..*self };
31
32 test_cx.run_debuginfo_cdb_test_no_opt();
33 }
34
35 fn run_debuginfo_cdb_test_no_opt(&self) {
36 let exe_file = self.make_exe_name();
37
38 let pdb_file = exe_file.with_extension(".pdb");
47 if pdb_file.exists() {
48 std::fs::remove_file(pdb_file).unwrap();
49 }
50
51 let should_run = self.run_if_enabled();
53 let compile_result = self.compile_test(should_run, Emit::None);
54 if !compile_result.status.success() {
55 self.fatal_proc_rec("compilation failed!", &compile_result);
56 }
57 if let WillExecute::Disabled = should_run {
58 return;
59 }
60
61 let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "cdb")
63 .unwrap_or_else(|e| self.fatal(&e));
64
65 let mut script_str = String::with_capacity(2048);
67 script_str.push_str("version\n"); script_str.push_str(".nvlist\n"); let mut js_extension = self.testpaths.file.clone();
73 js_extension.set_extension("cdb.js");
74 if js_extension.exists() {
75 script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension));
76 }
77
78 let source_file_name = self.testpaths.file.file_name().unwrap();
80 for line in &dbg_cmds.breakpoint_lines {
81 script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
82 }
83
84 for line in &dbg_cmds.commands {
86 script_str.push_str(line);
87 script_str.push('\n');
88 }
89
90 script_str.push_str("qq\n"); debug!("script_str = {}", script_str);
94 self.dump_output_file(&script_str, "debugger.script");
95 let debugger_script = self.make_out_name("debugger.script");
96
97 let cdb_path = &self.config.cdb.as_ref().unwrap();
98 let mut cdb = Command::new(cdb_path);
99 cdb.arg("-lines") .arg("-cf")
101 .arg(&debugger_script)
102 .arg(&exe_file);
103
104 let debugger_run_result = self.compose_and_run(
105 cdb,
106 self.config.run_lib_path.as_path(),
107 None, None, );
110
111 if !debugger_run_result.status.success() {
112 self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
113 }
114
115 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
116 self.fatal_proc_rec(&e, &debugger_run_result);
117 }
118 }
119
120 fn run_debuginfo_gdb_test(&self) {
121 let config = Config {
122 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
123 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
124 ..self.config.clone()
125 };
126
127 let test_cx = TestCx { config: &config, ..*self };
128
129 test_cx.run_debuginfo_gdb_test_no_opt();
130 }
131
132 fn run_debuginfo_gdb_test_no_opt(&self) {
133 let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "gdb")
134 .unwrap_or_else(|e| self.fatal(&e));
135 let mut cmds = dbg_cmds.commands.join("\n");
136
137 let should_run = self.run_if_enabled();
139 let compiler_run_result = self.compile_test(should_run, Emit::None);
140 if !compiler_run_result.status.success() {
141 self.fatal_proc_rec("compilation failed!", &compiler_run_result);
142 }
143 if let WillExecute::Disabled = should_run {
144 return;
145 }
146
147 let exe_file = self.make_exe_name();
148
149 let debugger_run_result;
150 if is_android_gdb_target(&self.config.target) {
151 cmds = cmds.replace("run", "continue");
152
153 let mut script_str = String::with_capacity(2048);
155 script_str.push_str(&format!("set charset {}\n", Self::charset()));
156 script_str.push_str(&format!("set sysroot {}\n", &self.config.android_cross_path));
157 script_str.push_str(&format!("file {}\n", exe_file));
158 script_str.push_str("target remote :5039\n");
159 script_str.push_str(&format!(
160 "set solib-search-path \
161 ./{}/stage2/lib/rustlib/{}/lib/\n",
162 self.config.host, self.config.target
163 ));
164 for line in &dbg_cmds.breakpoint_lines {
165 script_str.push_str(
166 format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line)
167 .as_str(),
168 );
169 }
170 script_str.push_str(&cmds);
171 script_str.push_str("\nquit\n");
172
173 debug!("script_str = {}", script_str);
174 self.dump_output_file(&script_str, "debugger.script");
175
176 let adb_path = &self.config.adb_path;
177
178 Command::new(adb_path)
179 .arg("push")
180 .arg(&exe_file)
181 .arg(&self.config.adb_test_dir)
182 .status()
183 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
184
185 Command::new(adb_path)
186 .args(&["forward", "tcp:5039", "tcp:5039"])
187 .status()
188 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
189
190 let adb_arg = format!(
191 "export LD_LIBRARY_PATH={}; \
192 gdbserver{} :5039 {}/{}",
193 self.config.adb_test_dir.clone(),
194 if self.config.target.contains("aarch64") { "64" } else { "" },
195 self.config.adb_test_dir.clone(),
196 exe_file.file_name().unwrap()
197 );
198
199 debug!("adb arg: {}", adb_arg);
200 let mut adb = Command::new(adb_path)
201 .args(&["shell", &adb_arg])
202 .stdout(Stdio::piped())
203 .stderr(Stdio::inherit())
204 .spawn()
205 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
206
207 let mut stdout = BufReader::new(adb.stdout.take().unwrap());
211 let mut line = String::new();
212 loop {
213 line.truncate(0);
214 stdout.read_line(&mut line).unwrap();
215 if line.starts_with("Listening on port 5039") {
216 break;
217 }
218 }
219 drop(stdout);
220
221 let mut debugger_script = OsString::from("-command=");
222 debugger_script.push(self.make_out_name("debugger.script"));
223 let debugger_opts: &[&OsStr] =
224 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
225
226 let gdb_path = self.config.gdb.as_ref().unwrap();
227 let Output { status, stdout, stderr } = Command::new(&gdb_path)
228 .args(debugger_opts)
229 .output()
230 .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
231 let cmdline = {
232 let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
233 gdb.args(debugger_opts);
234 let cmdline = self.make_cmdline(&gdb, Utf8Path::new(""));
236 self.logv(format_args!("executing {cmdline}"));
237 cmdline
238 };
239
240 debugger_run_result = ProcRes {
241 status,
242 stdout: String::from_utf8(stdout).unwrap(),
243 stderr: String::from_utf8(stderr).unwrap(),
244 truncated: Truncated::No,
245 cmdline,
246 };
247 if adb.kill().is_err() {
248 writeln!(self.stdout, "Adb process is already finished.");
249 }
250 } else {
251 let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
252 let mut script_str = String::with_capacity(2048);
254 script_str.push_str(&format!("set charset {}\n", Self::charset()));
255 script_str.push_str("show version\n");
256
257 match self.config.gdb_version {
258 Some(version) => {
259 writeln!(
260 self.stdout,
261 "NOTE: compiletest thinks it is using GDB version {}",
262 version
263 );
264
265 if !self.props.disable_gdb_pretty_printers
266 && version > extract_gdb_version("7.4").unwrap()
267 {
268 script_str.push_str(&format!(
271 "add-auto-load-safe-path {}\n",
272 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
273 ));
274
275 script_str.push_str(&format!(
279 "add-auto-load-safe-path {}\n",
280 self.output_base_dir().as_str().replace(r"\", r"\\")
281 ));
282 }
283 }
284 _ => {
285 writeln!(
286 self.stdout,
287 "NOTE: compiletest does not know which version of \
288 GDB it is using"
289 );
290 }
291 }
292
293 script_str.push_str("set print pretty off\n");
296
297 script_str.push_str(&format!(
299 "directory {}\n",
300 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
301 ));
302
303 script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\")));
305
306 script_str.push_str("set language rust\n");
308
309 for line in &dbg_cmds.breakpoint_lines {
311 script_str.push_str(&format!(
312 "break '{}':{}\n",
313 self.testpaths.file.file_name().unwrap(),
314 *line
315 ));
316 }
317
318 script_str.push_str(&cmds);
319 script_str.push_str("\nquit\n");
320
321 debug!("script_str = {}", script_str);
322 self.dump_output_file(&script_str, "debugger.script");
323
324 let mut debugger_script = OsString::from("-command=");
325 debugger_script.push(self.make_out_name("debugger.script"));
326
327 let debugger_opts: &[&OsStr] =
328 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
329
330 let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
331
332 let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
334 format!("{pp}:{rust_pp_module_abs_path}")
335 } else {
336 rust_pp_module_abs_path.to_string()
337 };
338 gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
339
340 debugger_run_result =
341 self.compose_and_run(gdb, self.config.run_lib_path.as_path(), None, None);
342 }
343
344 if !debugger_run_result.status.success() {
345 self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
346 }
347
348 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
349 self.fatal_proc_rec(&e, &debugger_run_result);
350 }
351 }
352
353 fn run_debuginfo_lldb_test(&self) {
354 if self.config.lldb_python_dir.is_none() {
355 self.fatal("Can't run LLDB test because LLDB's python path is not set.");
356 }
357
358 let config = Config {
359 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
360 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
361 ..self.config.clone()
362 };
363
364 let test_cx = TestCx { config: &config, ..*self };
365
366 test_cx.run_debuginfo_lldb_test_no_opt();
367 }
368
369 fn run_debuginfo_lldb_test_no_opt(&self) {
370 let should_run = self.run_if_enabled();
372 let compile_result = self.compile_test(should_run, Emit::None);
373 if !compile_result.status.success() {
374 self.fatal_proc_rec("compilation failed!", &compile_result);
375 }
376 if let WillExecute::Disabled = should_run {
377 return;
378 }
379
380 let exe_file = self.make_exe_name();
381
382 match self.config.lldb_version {
383 Some(ref version) => {
384 writeln!(
385 self.stdout,
386 "NOTE: compiletest thinks it is using LLDB version {}",
387 version
388 );
389 }
390 _ => {
391 writeln!(
392 self.stdout,
393 "NOTE: compiletest does not know which version of \
394 LLDB it is using"
395 );
396 }
397 }
398
399 let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, "lldb")
401 .unwrap_or_else(|e| self.fatal(&e));
402
403 let mut script_str = String::from("settings set auto-confirm true\n");
406
407 if self.config.host.contains("darwin") {
433 script_str.push_str("settings set target.inherit-tcc true\n");
434 }
435
436 script_str.push_str("version\n");
438
439 let rust_pp_module_abs_path = self.config.src_root.join("src/etc");
441
442 script_str.push_str(&format!(
443 "command script import {}/lldb_lookup.py\n",
444 rust_pp_module_abs_path
445 ));
446 File::open(rust_pp_module_abs_path.join("lldb_commands"))
447 .and_then(|mut file| file.read_to_string(&mut script_str))
448 .expect("Failed to read lldb_commands");
449
450 let source_file_name = self.testpaths.file.file_name().unwrap();
452 for line in &dbg_cmds.breakpoint_lines {
453 script_str.push_str(&format!(
454 "breakpoint set --file '{}' --line {}\n",
455 source_file_name, line
456 ));
457 }
458
459 for line in &dbg_cmds.commands {
461 script_str.push_str(line);
462 script_str.push('\n');
463 }
464
465 script_str.push_str("\nquit\n");
467
468 debug!("script_str = {}", script_str);
470 self.dump_output_file(&script_str, "debugger.script");
471 let debugger_script = self.make_out_name("debugger.script");
472
473 let debugger_run_result = self.run_lldb(&exe_file, &debugger_script);
475
476 if !debugger_run_result.status.success() {
477 self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
478 }
479
480 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
481 self.fatal_proc_rec(&e, &debugger_run_result);
482 }
483 }
484
485 fn run_lldb(&self, test_executable: &Utf8Path, debugger_script: &Utf8Path) -> ProcRes {
486 let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py");
488
489 let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
491 format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap())
492 } else {
493 self.config.lldb_python_dir.clone().unwrap()
494 };
495 self.run_command_to_procres(
496 Command::new(&self.config.python)
497 .arg(&lldb_script_path)
498 .arg(test_executable)
499 .arg(debugger_script)
500 .env("PYTHONUNBUFFERED", "1") .env("PYTHONPATH", pythonpath),
502 )
503 }
504
505 fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> {
506 let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
508
509 options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect()
510 }
511}