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