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, is_android_gdb_target};
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")
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.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")
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 is_android_gdb_target(&self.config.target) {
126 cmds = cmds.replace("run", "continue");
127
128 let mut script_str = String::with_capacity(2048);
130 script_str.push_str(&format!("set charset {}\n", Self::charset()));
131 script_str.push_str(&format!("set sysroot {}\n", &self.config.android_cross_path));
132 script_str.push_str(&format!("file {}\n", exe_file));
133 script_str.push_str("target remote :5039\n");
134 script_str.push_str(&format!(
135 "set solib-search-path \
136 ./{}/stage2/lib/rustlib/{}/lib/\n",
137 self.config.host, self.config.target
138 ));
139 for line in &dbg_cmds.breakpoint_lines {
140 script_str.push_str(
141 format!("break {}:{}\n", self.testpaths.file.file_name().unwrap(), *line)
142 .as_str(),
143 );
144 }
145 script_str.push_str(&cmds);
146 script_str.push_str("\nquit\n");
147
148 debug!("script_str = {}", script_str);
149 self.dump_output_file(&script_str, "debugger.script");
150
151 let adb_path = &self.config.adb_path;
152
153 Command::new(adb_path)
154 .arg("push")
155 .arg(&exe_file)
156 .arg(&self.config.adb_test_dir)
157 .status()
158 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
159
160 Command::new(adb_path)
161 .args(&["forward", "tcp:5039", "tcp:5039"])
162 .status()
163 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
164
165 let adb_arg = format!(
166 "export LD_LIBRARY_PATH={}; \
167 gdbserver{} :5039 {}/{}",
168 self.config.adb_test_dir.clone(),
169 if self.config.target.contains("aarch64") { "64" } else { "" },
170 self.config.adb_test_dir.clone(),
171 exe_file.file_name().unwrap()
172 );
173
174 debug!("adb arg: {}", adb_arg);
175 let mut adb = Command::new(adb_path)
176 .args(&["shell", &adb_arg])
177 .stdout(Stdio::piped())
178 .stderr(Stdio::inherit())
179 .spawn()
180 .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}"));
181
182 let mut stdout = BufReader::new(adb.stdout.take().unwrap());
186 let mut line = String::new();
187 loop {
188 line.truncate(0);
189 stdout.read_line(&mut line).unwrap();
190 if line.starts_with("Listening on port 5039") {
191 break;
192 }
193 }
194 drop(stdout);
195
196 let mut debugger_script = OsString::from("-command=");
197 debugger_script.push(self.make_out_name("debugger.script"));
198 let debugger_opts: &[&OsStr] =
199 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
200
201 let gdb_path = self.config.gdb.as_ref().unwrap();
202 let Output { status, stdout, stderr } = Command::new(&gdb_path)
203 .args(debugger_opts)
204 .output()
205 .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}"));
206 let cmdline = {
207 let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
208 gdb.args(debugger_opts);
209 let cmdline = self.make_cmdline(&gdb, Utf8Path::new(""));
211 self.logv(format_args!("executing {cmdline}"));
212 cmdline
213 };
214
215 debugger_run_result = ProcRes {
216 status,
217 stdout: String::from_utf8(stdout).unwrap(),
218 stderr: String::from_utf8(stderr).unwrap(),
219 truncated: Truncated::No,
220 cmdline,
221 };
222 if adb.kill().is_err() {
223 writeln!(self.stdout, "Adb process is already finished.");
224 }
225 } else {
226 let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
227 let mut script_str = String::with_capacity(2048);
229 script_str.push_str(&format!("set charset {}\n", Self::charset()));
230 script_str.push_str("show version\n");
231
232 match self.config.gdb_version {
233 Some(version) => {
234 writeln!(
235 self.stdout,
236 "NOTE: compiletest thinks it is using GDB version {}",
237 version
238 );
239
240 if !self.props.disable_gdb_pretty_printers
241 && version > extract_gdb_version("7.4").unwrap()
242 {
243 script_str.push_str(&format!(
246 "add-auto-load-safe-path {}\n",
247 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
248 ));
249
250 script_str.push_str(&format!(
254 "add-auto-load-safe-path {}\n",
255 self.output_base_dir().as_str().replace(r"\", r"\\")
256 ));
257 }
258 }
259 _ => {
260 writeln!(
261 self.stdout,
262 "NOTE: compiletest does not know which version of \
263 GDB it is using"
264 );
265 }
266 }
267
268 script_str.push_str("set print pretty off\n");
271
272 script_str.push_str(&format!(
274 "directory {}\n",
275 rust_pp_module_abs_path.as_str().replace(r"\", r"\\")
276 ));
277
278 script_str.push_str(&format!("file {}\n", exe_file.as_str().replace(r"\", r"\\")));
280
281 script_str.push_str("set language rust\n");
283
284 for line in &dbg_cmds.breakpoint_lines {
286 script_str.push_str(&format!(
287 "break '{}':{}\n",
288 self.testpaths.file.file_name().unwrap(),
289 *line
290 ));
291 }
292
293 script_str.push_str(&cmds);
294 script_str.push_str("\nquit\n");
295
296 debug!("script_str = {}", script_str);
297 self.dump_output_file(&script_str, "debugger.script");
298
299 let mut debugger_script = OsString::from("-command=");
300 debugger_script.push(self.make_out_name("debugger.script"));
301
302 let debugger_opts: &[&OsStr] =
303 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
304
305 let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
306
307 let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
309 format!("{pp}:{rust_pp_module_abs_path}")
310 } else {
311 rust_pp_module_abs_path.to_string()
312 };
313 gdb.args(debugger_opts).env("PYTHONPATH", pythonpath);
314
315 debugger_run_result =
316 self.compose_and_run(gdb, self.config.run_lib_path.as_path(), None, None);
317 }
318
319 if !debugger_run_result.status.success() {
320 self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
321 }
322
323 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
324 self.fatal_proc_rec(&e, &debugger_run_result);
325 }
326 }
327
328 fn run_debuginfo_lldb_test(&self) {
329 if self.config.lldb_python_dir.is_none() {
330 self.fatal("Can't run LLDB test because LLDB's python path is not set.");
331 }
332
333 let should_run = self.run_if_enabled();
335 let compile_result = self.compile_test(should_run, Emit::None);
336 if !compile_result.status.success() {
337 self.fatal_proc_rec("compilation failed!", &compile_result);
338 }
339 if let WillExecute::Disabled = should_run {
340 return;
341 }
342
343 let exe_file = self.make_exe_name();
344
345 match self.config.lldb_version {
346 Some(ref version) => {
347 writeln!(
348 self.stdout,
349 "NOTE: compiletest thinks it is using LLDB version {}",
350 version
351 );
352 }
353 _ => {
354 writeln!(
355 self.stdout,
356 "NOTE: compiletest does not know which version of \
357 LLDB it is using"
358 );
359 }
360 }
361
362 let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, "lldb")
364 .unwrap_or_else(|e| self.fatal(&e));
365
366 let mut script_str = String::from("settings set auto-confirm true\n");
369
370 if self.config.host.contains("darwin") {
396 script_str.push_str("settings set target.inherit-tcc true\n");
397 }
398
399 script_str.push_str("version\n");
401
402 let rust_pp_module_abs_path = self.config.src_root.join("src/etc");
404
405 script_str.push_str(&format!(
406 "command script import {}/lldb_lookup.py\n",
407 rust_pp_module_abs_path
408 ));
409 File::open(rust_pp_module_abs_path.join("lldb_commands"))
410 .and_then(|mut file| file.read_to_string(&mut script_str))
411 .expect("Failed to read lldb_commands");
412
413 let source_file_name = self.testpaths.file.file_name().unwrap();
415 for line in &dbg_cmds.breakpoint_lines {
416 script_str.push_str(&format!(
417 "breakpoint set --file '{}' --line {}\n",
418 source_file_name, line
419 ));
420 }
421
422 for line in &dbg_cmds.commands {
424 script_str.push_str(line);
425 script_str.push('\n');
426 }
427
428 script_str.push_str("\nquit\n");
430
431 debug!("script_str = {}", script_str);
433 self.dump_output_file(&script_str, "debugger.script");
434 let debugger_script = self.make_out_name("debugger.script");
435
436 let debugger_run_result = self.run_lldb(&exe_file, &debugger_script);
438
439 if !debugger_run_result.status.success() {
440 self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
441 }
442
443 if let Err(e) = dbg_cmds.check_output(&debugger_run_result) {
444 self.fatal_proc_rec(&e, &debugger_run_result);
445 }
446 }
447
448 fn run_lldb(&self, test_executable: &Utf8Path, debugger_script: &Utf8Path) -> ProcRes {
449 let lldb_script_path = self.config.src_root.join("src/etc/lldb_batchmode.py");
451
452 let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") {
454 format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap())
455 } else {
456 self.config.lldb_python_dir.clone().unwrap()
457 };
458 self.run_command_to_procres(
459 Command::new(&self.config.python)
460 .arg(&lldb_script_path)
461 .arg(test_executable)
462 .arg(debugger_script)
463 .env("PYTHONUNBUFFERED", "1") .env("PYTHONPATH", pythonpath),
465 )
466 }
467}