1use std::ffi::{OsStr, OsString};
2use std::io::{BufRead, BufReader};
3use std::process::{Command, Output, Stdio};
4
5use camino::Utf8Path;
6use tracing::debug;
7
8use super::debugger::DebuggerCommands;
9use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
10use crate::debuggers::extract_gdb_version;
11use crate::util::ArgFileCommand;
12
13impl TestCx<'_> {
14 pub(super) fn run_debuginfo_test(&self) {
15 match self.variant.debugger.as_ref().unwrap() {
16 Debugger::Cdb => self.run_debuginfo_cdb_test(),
17 Debugger::Gdb => self.run_debuginfo_gdb_test(),
18 Debugger::Lldb => self.run_debuginfo_lldb_test(),
19 }
20 }
21
22 fn run_debuginfo_cdb_test(&self) {
23 let exe_file = self.make_exe_name();
24
25 let pdb_file = exe_file.with_extension(".pdb");
34 if pdb_file.exists() {
35 std::fs::remove_file(pdb_file).unwrap();
36 }
37
38 let should_run = self.run_if_enabled();
40 let compile_result = self.compile_test(should_run, Emit::None);
41 if !compile_result.status.success() {
42 self.fatal_proc_rec("compilation failed!", &compile_result);
43 }
44 if let WillExecute::Disabled = should_run {
45 return;
46 }
47
48 let dbg_cmds =
50 DebuggerCommands::parse_from(&self.testpaths.file, "cdb", self.variant.revision())
51 .unwrap_or_else(|e| self.fatal(&e));
52
53 let mut script_str = String::with_capacity(2048);
55 script_str.push_str("version\n"); script_str.push_str(".nvlist\n"); let mut js_extension = self.testpaths.file.clone();
61 js_extension.set_extension("cdb.js");
62 if js_extension.exists() {
63 script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension));
64 }
65
66 let source_file_name = self.testpaths.file.file_name().unwrap();
68 for line in &dbg_cmds.breakpoint_lines {
69 script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
70 }
71
72 for line in &dbg_cmds.commands {
74 script_str.push_str(line);
75 script_str.push('\n');
76 }
77
78 script_str.push_str("qq\n"); debug!("script_str = {}", script_str);
82 self.dump_output_file(&script_str, "debugger.script");
83 let debugger_script = self.make_out_name("debugger.script");
84
85 let cdb_path = &self.config.cdb.as_ref().unwrap();
86 let mut cdb = Command::new(cdb_path);
87 cdb.arg("-lines") .arg("-cf")
89 .arg(&debugger_script)
90 .arg(&exe_file);
91
92 let debugger_run_result = self.compose_and_run(
93 cdb,
94 self.config.target_run_lib_path.as_path(),
95 None, None, );
98
99 if !debugger_run_result.status.success() {
100 self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
101 }
102
103 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
104 self.fatal_proc_rec(&e, &debugger_run_result);
105 }
106 }
107
108 fn run_debuginfo_gdb_test(&self) {
109 let dbg_cmds =
110 DebuggerCommands::parse_from(&self.testpaths.file, "gdb", self.variant.revision())
111 .unwrap_or_else(|e| self.fatal(&e));
112 let mut cmds = dbg_cmds.commands.join("\n");
113
114 let should_run = self.run_if_enabled();
116 let compiler_run_result = self.compile_test(should_run, Emit::None);
117 if !compiler_run_result.status.success() {
118 self.fatal_proc_rec("compilation failed!", &compiler_run_result);
119 }
120 if let WillExecute::Disabled = should_run {
121 return;
122 }
123
124 let exe_file = self.make_exe_name();
125
126 let debugger_run_result;
127 if let Some(android_cross_path) = self.config.android_cross_path.as_deref() {
130 cmds = cmds.replace("run", "continue");
131
132 let mut script_str = String::with_capacity(2048);
134 script_str.push_str(&format!("set charset {}\n", Self::charset()));
135 script_str.push_str(&format!("set sysroot {android_cross_path}\n"));
136 script_str.push_str(&format!("file {}\n", exe_file));
137 script_str.push_str("target remote :5039\n");
138 script_str.push_str(&format!(
139 "set solib-search-path \
140 ./{}/stage2/lib/rustlib/{}/lib/\n",
141 self.config.host, self.config.target
142 ));
143 for line in &dbg_cmds.breakpoint_lines {
144 script_str.push_str(
145 format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line)
146 .as_str(),
147 );
148 }
149 script_str.push_str(&cmds);
150 script_str.push_str("\nquit\n");
151
152 debug!("script_str = {}", script_str);
153 self.dump_output_file(&script_str, "debugger.script");
154
155 let adb_path = self.config.adb_path.as_ref().expect("`adb_path` must be specified");
158 let adb_test_dir =
159 self.config.adb_test_dir.as_ref().expect("`adb_test_dir` must be specified");
160
161 Command::new(adb_path)
162 .arg("push")
163 .arg(&exe_file)
164 .arg(adb_test_dir)
165 .status()
166 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
167
168 Command::new(adb_path)
169 .args(&["forward", "tcp:5039", "tcp:5039"])
170 .status()
171 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
172
173 let adb_arg = format!(
174 "export LD_LIBRARY_PATH={}; \
175 gdbserver{} :5039 {}/{}",
176 adb_test_dir,
177 if self.config.target.contains("aarch64") { "64" } else { "" },
178 adb_test_dir,
179 exe_file.file_name().unwrap()
180 );
181
182 debug!("adb arg: {}", adb_arg);
183 let mut adb = Command::new(adb_path)
184 .args(&["shell", &adb_arg])
185 .stdout(Stdio::piped())
186 .stderr(Stdio::inherit())
187 .spawn()
188 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
189
190 let mut stdout = BufReader::new(adb.stdout.take().unwrap());
194 let mut line = String::new();
195 loop {
196 line.clear();
197 stdout.read_line(&mut line).unwrap();
198 if line.starts_with("Listening on port 5039") {
199 break;
200 }
201 }
202 drop(stdout);
203
204 let mut debugger_script = OsString::from("-command=");
205 debugger_script.push(self.make_out_name("debugger.script"));
206 let debugger_opts: &[&OsStr] =
207 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
208
209 let gdb_path = self.config.gdb.as_ref().unwrap();
210 let Output { status, stdout, stderr } = Command::new(&gdb_path)
211 .args(debugger_opts)
212 .output()
213 .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
214 let cmdline = {
215 let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
216 gdb.args(debugger_opts);
217 let cmdline = self.make_cmdline(&gdb, Utf8Path::new(""));
219 self.logv(format_args!("executing {cmdline}"));
220 cmdline
221 };
222
223 debugger_run_result = ProcRes {
224 status,
225 stdout: String::from_utf8(stdout).unwrap(),
226 stderr: String::from_utf8(stderr).unwrap(),
227 truncated: Truncated::No,
228 cmdline,
229 };
230 if adb.kill().is_err() {
231 writeln!(self.stdout, "Adb process is already finished.");
232 }
233 } else {
234 let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
235 let mut script_str = String::with_capacity(2048);
237 script_str.push_str(&format!("set charset {}\n", Self::charset()));
238 script_str.push_str("show version\n");
239
240 match self.config.gdb_version {
241 Some(version) => {
242 writeln!(
243 self.stdout,
244 "NOTE: compiletest thinks it is using GDB version {}",
245 version
246 );
247
248 if !self.props.disable_gdb_pretty_printers
249 && version > extract_gdb_version("7.4").unwrap()
250 {
251 script_str.push_str(&format!(
254 "add-auto-load-safe-path {}\n",
255 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
256 ));
257
258 script_str.push_str(&format!(
262 "add-auto-load-safe-path {}\n",
263 self.output_base_dir().as_str().replace(r"\", r"\\")
264 ));
265
266 #[cfg(target_os = "windows")]
270 {
271 script_str.push_str(&format!(
272 "source {}\n",
273 self.config
274 .src_root
275 .join("src/etc/gdb_load_rust_pretty_printers.py")
276 ));
277 }
278 }
279 }
280 _ => {
281 writeln!(
282 self.stdout,
283 "NOTE: compiletest does not know which version of \
284 GDB it is using"
285 );
286 }
287 }
288
289 script_str.push_str("set print pretty off\n");
292
293 script_str.push_str(&format!(
295 "directory {}\n",
296 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
297 ));
298
299 script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\")));
301
302 script_str.push_str("set language rust\n");
304
305 for line in &dbg_cmds.breakpoint_lines {
307 script_str.push_str(&format!(
308 "break '{}':{}\n",
309 self.testpaths.file.file_name().unwrap(),
310 *line
311 ));
312 }
313
314 script_str.push_str(&cmds);
315 script_str.push_str("\nquit\n");
316
317 debug!("script_str = {}", script_str);
318 self.dump_output_file(&script_str, "debugger.script");
319
320 let mut debugger_script = OsString::from("-command=");
321 debugger_script.push(self.make_out_name("debugger.script"));
322
323 let debugger_opts: &[&OsStr] =
324 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
325
326 let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
327
328 let pythonpath = with_pythonpath_prepended(&rust_pp_module_abs_path);
329 gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
330
331 debugger_run_result =
332 self.compose_and_run(gdb, self.config.target_run_lib_path.as_path(), None, None);
333 }
334
335 if !debugger_run_result.status.success() {
336 self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
337 }
338
339 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
340 self.fatal_proc_rec(&e, &debugger_run_result);
341 }
342 }
343
344 fn run_debuginfo_lldb_test(&self) {
345 let Some(ref lldb) = self.config.lldb else {
346 self.fatal("Can't run LLDB test because LLDB's path is not set.");
347 };
348
349 let should_run = self.run_if_enabled();
351 let compile_result = self.compile_test(should_run, Emit::None);
352 if !compile_result.status.success() {
353 self.fatal_proc_rec("compilation failed!", &compile_result);
354 }
355 if let WillExecute::Disabled = should_run {
356 return;
357 }
358
359 let exe_file = self.make_exe_name();
360
361 match self.config.lldb_version {
362 Some(ref version) => {
363 writeln!(
364 self.stdout,
365 "NOTE: compiletest thinks it is using LLDB version: {:?}",
366 version
367 );
368 }
369 _ => {
370 writeln!(
371 self.stdout,
372 "NOTE: compiletest does not know which version of \
373 LLDB it is using"
374 );
375 }
376 }
377
378 let dbg_cmds =
380 DebuggerCommands::parse_from(&self.testpaths.file, "lldb", self.variant.revision())
381 .unwrap_or_else(|e| self.fatal(&e));
382
383 let mut script_str = String::from("settings set auto-confirm true\n");
386
387 if self.config.host.contains("darwin") {
413 script_str.push_str("settings set target.inherit-tcc true\n");
414 }
415
416 script_str.push_str("version\n");
418
419 let rust_pp_module_abs_path = self.config.src_root.join("src/etc");
421
422 script_str.push_str(&format!(
423 "command script import {}/lldb_lookup.py\n",
424 rust_pp_module_abs_path
425 ));
426 script_str.push_str("script print(lldb_lookup.FEATURE_FLAGS)\n");
427
428 let source_file_name = self.testpaths.file.file_name().unwrap();
430 for line in &dbg_cmds.breakpoint_lines {
431 script_str.push_str(&format!(
432 "breakpoint set --file '{}' --line {}\n",
433 source_file_name, line
434 ));
435 }
436
437 for line in &dbg_cmds.commands {
439 script_str.push_str(line);
440 script_str.push('\n');
441 }
442
443 script_str.push_str("\nquit\n");
445
446 debug!("script_str = {}", script_str);
448 self.dump_output_file(&script_str, "debugger.script");
449 let debugger_script = self.make_out_name("debugger.script");
450
451 let debugger_run_result = self.run_lldb(lldb, &exe_file, &debugger_script);
453
454 if !debugger_run_result.status.success() {
455 self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
456 }
457
458 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
459 self.fatal_proc_rec(&e, &debugger_run_result);
460 }
461 }
462
463 fn run_lldb(
464 &self,
465 lldb: &Utf8Path,
466 test_executable: &Utf8Path,
467 debugger_script: &Utf8Path,
468 ) -> ProcRes {
469 let rust_pp_module_abs_path = self.config.src_root.join("src/etc");
471 let pythonpath = with_pythonpath_prepended(&rust_pp_module_abs_path);
472 let path = prepend_to_path(&self.config.target_run_lib_path);
474
475 let mut cmd = ArgFileCommand::new(lldb);
476 cmd.arg("--one-line")
477 .arg("script --language python -- import lldb_batchmode; lldb_batchmode.main()")
478 .env("LLDB_BATCHMODE_TARGET_PATH", test_executable)
479 .env("LLDB_BATCHMODE_SCRIPT_PATH", debugger_script)
480 .env("PYTHONUNBUFFERED", "1") .env("PYTHONPATH", pythonpath)
482 .env("PATH", path);
483
484 self.run_command_to_procres(cmd)
485 }
486}
487
488fn with_pythonpath_prepended(some_path: &Utf8Path) -> String {
489 if let Ok(pp) = std::env::var("PYTHONPATH") {
491 #[cfg(target_os = "windows")]
492 {
493 format!("{pp};{some_path}")
494 }
495 #[cfg(not(target_os = "windows"))]
496 {
497 format!("{pp}:{some_path}")
498 }
499 } else {
500 some_path.to_string()
501 }
502}
503
504fn prepend_to_path(some_path: &Utf8Path) -> String {
505 if let Ok(path) = std::env::var("PATH") {
506 #[cfg(target_os = "windows")]
507 {
508 format!("{some_path};{path}")
509 }
510 #[cfg(not(target_os = "windows"))]
511 {
512 format!("{some_path}:{path}")
513 }
514 } else {
515 some_path.to_string()
516 }
517}