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