Mac lldb debuginfo behavior differs based on abs/rel input source path (?!)
I am observing varying behavior on my Mac depending on whether the input source files to rustc are denoted via an absolute source path or a relative one.
The test where I am seeing varying behavior is the cross-crate-spans.rs debuginfo test: https://github.com/rust-lang/rust/blob/master/src/test/debuginfo/cross-crate-spans.rs
In a nutshell: when I need to make a breakpoint for a line in the auxiliary crate (the auxiliary/cross_crate_spans.rs in the test in question), I am seeing the breakpoint get hit twice (as expected) when I use a relative paths, but the breakpoint gets hit only once when I use absolute paths to compile the same input files.
I have made a shell script to establish the context for the problem:
set -e
# set -x
RUSTC=rustc
# We'll set up and run the test in an isolated directory just to be safe.
# (Note: needs to be an absolute path for the rest of the script to make sense...)
WORKING_DIR=/tmp/work
mkdir -p $WORKING_DIR
cd $WORKING_DIR
# The behavior we observe will vary depending on the value of
# `$SRC_DIR`. The bad behavior seems to arise whenever SRC_DIR is an
# absolute directory (i.e. whenever it leads with the '/' character.)
case $1 in
abs) SRC1_DIR=$WORKING_DIR/abs/source ; echo "Aux in absolute source path: $SRC1_DIR" ;;
rel) SRC1_DIR=rel/source ; echo "Aux in relative source path: $SRC1_DIR" ;;
*) echo "You need to pass 'abs' or 'rel' as first input to script."; exit 1;
esac
case $2 in
abs) SRC2_DIR=$WORKING_DIR/abs/source ; echo "Main in absolute source path: $SRC2_DIR" ;;
# (switching to the below fixes things on my host system.)
rel) SRC2_DIR=rel/source ; echo "Main in relative source path: $SRC2_DIR" ;;
*) echo "You need to pass 'abs' or 'rel' as second input to script."; exit 1;
esac
mkdir -p $SRC1_DIR
mkdir -p $SRC2_DIR
# (I renamed the aux file because the dash/underscore differentiating
# convention drove me crazy. However, I have observed that reproducing
# tne bug *also* is sensitive to maintaining the specific filename
# `cross-crate-spans.rs` for the main file. Perhaps there is something
# with how we pun dash and underscore when inferring crate names from
# file names?)
AUX_FILE=$SRC1_DIR/aux.rs
MAIN_FILE=$SRC2_DIR/cross-crate-spans.rs
# Move the files into the source area. (You don't have to use `curl`;
# you can just copy it from your own repository...)
curl -s -o $AUX_FILE -O https://raw.githubusercontent.com/rust-lang/rust/8b1941a7831d715e36a414668a0b23146ff9cc2c/src/test/debuginfo/auxiliary/cross_crate_spans.rs
curl -s -o $MAIN_FILE -O https://raw.githubusercontent.com/rust-lang/rust/8b1941a7831d715e36a414668a0b23146ff9cc2c/src/test/debuginfo/cross-crate-spans.rs
LIB_DIR=$WORKING_DIR/libs
mkdir -p $LIB_DIR
MAIN_BIN=cross-crate-spans
# (Show how the compiler is being invoked in each case.)
set -x
$RUSTC -C opt-level=0 -g $AUX_FILE --crate-name cross_crate_spans --out-dir $LIB_DIR
$RUSTC -C opt-level=0 -g $MAIN_FILE -L $LIB_DIR -C prefer-dynamic -C rpath -o $MAIN_BIN
# $RUSTC -C opt-level=0 -g $MAIN_FILE -L $LIB_DIR -o $MAIN_BIN
lldb $MAIN_BIN
After invoking the above script (e.g. via sh /tmp/script abs abs), you then interact with lldb to finish reproducing the bug.
% rm -rf /tmp/work/ && sh /tmp/script rel rel
Aux in relative source path: rel/source
Main in relative source path: rel/source
+ rustc -C opt-level=0 -g rel/source/aux.rs --crate-name cross_crate_spans --out-dir /tmp/work/libs
+ rustc -C opt-level=0 -g rel/source/cross-crate-spans.rs -L /tmp/work/libs -C prefer-dynamic -C rpath -o cross-crate-spans
+ lldb cross-crate-spans
(lldb) target create "cross-crate-spans"
Current executable set to 'cross-crate-spans' (x86_64).
(lldb) b aux.rs:24
Breakpoint 1: 2 locations.
(lldb) r
Process 4893 launched: '/private/tmp/work/cross-crate-spans' (x86_64)
Process 4893 stopped
* thread #1: tid = 0xad2057, 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
frame #0: 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4893 resuming
Process 4893 stopped
* thread #1: tid = 0xad2057, 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4893 resuming
(lldb) Process 4893 exited with status = 0 (0x00000000)
(lldb) q
% rm -rf /tmp/work/ && sh /tmp/script rel abs
Aux in relative source path: rel/source
Main in absolute source path: /tmp/work/abs/source
+ rustc -C opt-level=0 -g rel/source/aux.rs --crate-name cross_crate_spans --out-dir /tmp/work/libs
+ rustc -C opt-level=0 -g /tmp/work/abs/source/cross-crate-spans.rs -L /tmp/work/libs -C prefer-dynamic -C rpath -o cross-crate-spans
+ lldb cross-crate-spans
(lldb) target create "cross-crate-spans"
Current executable set to 'cross-crate-spans' (x86_64).
(lldb) b aux.rs:24
Breakpoint 1: 2 locations.
(lldb) r
Process 4913 launched: '/private/tmp/work/cross-crate-spans' (x86_64)
Process 4913 stopped
* thread #1: tid = 0xad20ff, 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
frame #0: 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4913 resuming
Process 4913 stopped
* thread #1: tid = 0xad20ff, 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4913 resuming
(lldb) Process 4913 exited with status = 0 (0x00000000)
(lldb) q
% rm -rf /tmp/work/ && sh /tmp/script abs rel
Aux in absolute source path: /tmp/work/abs/source
Main in relative source path: rel/source
+ rustc -C opt-level=0 -g /tmp/work/abs/source/aux.rs --crate-name cross_crate_spans --out-dir /tmp/work/libs
+ rustc -C opt-level=0 -g rel/source/cross-crate-spans.rs -L /tmp/work/libs -C prefer-dynamic -C rpath -o cross-crate-spans
+ lldb cross-crate-spans
(lldb) target create "cross-crate-spans"
Current executable set to 'cross-crate-spans' (x86_64).
(lldb) b aux.rs:24
Breakpoint 1: 2 locations.
(lldb) r
Process 4933 launched: '/private/tmp/work/cross-crate-spans' (x86_64)
Process 4933 stopped
* thread #1: tid = 0xad2163, 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2
frame #0: 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4933 resuming
Process 4933 stopped
* thread #1: tid = 0xad2163, 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000b4f cross-crate-spans`cross_crate_spans::generic_function<i16>(val=1212) + 127 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4933 resuming
(lldb) Process 4933 exited with status = 0 (0x00000000)
(lldb) q
% rm -rf /tmp/work/ && sh /tmp/script abs abs
Aux in absolute source path: /tmp/work/abs/source
Main in absolute source path: /tmp/work/abs/source
+ rustc -C opt-level=0 -g /tmp/work/abs/source/aux.rs --crate-name cross_crate_spans --out-dir /tmp/work/libs
+ rustc -C opt-level=0 -g /tmp/work/abs/source/cross-crate-spans.rs -L /tmp/work/libs -C prefer-dynamic -C rpath -o cross-crate-spans
+ lldb cross-crate-spans
(lldb) target create "cross-crate-spans"
Current executable set to 'cross-crate-spans' (x86_64).
(lldb) b aux.rs:24
Breakpoint 1: 2 locations.
(lldb) r
Process 4955 launched: '/private/tmp/work/cross-crate-spans' (x86_64)
Process 4955 stopped
* thread #1: tid = 0xad21fe, 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000c43 cross-crate-spans`cross_crate_spans::generic_function<u32>(val=17) + 115 at aux.rs:24
21 let result = (val.clone(), val.clone());
22 let a_variable: u32 = 123456789;
23 let another_variable: f64 = 123456789.5;
-> 24 zzz();
25 result
26 }
27
(lldb) c
Process 4955 resuming
(lldb) Process 4955 exited with status = 0 (0x00000000)
(lldb) q
%
The transcript above shows that the number of times we hit the breakpoint varies depending on how the source file path was provided.
The results are as follows when you vary the absolute/relative nature of each of the two source inputs in the script. (For some period of time, I thought I was observing that problem occurred just based on the main input, but by the time I firmed up the script above, I no longer was seeing that: I had to pass both the aux and the main input as absolute paths in order to reproduce the bug.)
| Aux Input | Main Input | Breakpoint Hit Count | Expected Result? | --- | --- | --- | --- | | rel | rel | 2 | (good) | rel | abs | 2 | (good) | abs | rel | 2 | (good) | abs | abs | 1 | bad
One more observation: By chance, I decided while looking into this to also try varying the input file names. This led to a new bit of data: reproducing the bug requires the main file keep the source cross-crate-spans.rs (and do not pass a --crate-name argument to it).
- If you rename it to
main.rs(and do not otherwise muck with its crate name), the bug will not reproduce. - If you rename it to
main.rsbut also pass--crate-name cross_crate_spans(the crate name derived from "cross-crate-spans.rs") when compilingmain.rs, then the bug will reproduce. - This leads me to wonder: Is the real bug here that we are blithely allowing multiple crates with the same name to be linked together, and even treating it like a scenario that needs to be part to our test suite?
My lldb reports that it is version lldb-360.1.70, and my Xcode (according to xcodebuild) is Xcode 8.2.1, Build version 8C1002.
cc @michaelwoerister
Felix S Klock II at 2017-03-24 13:52:07
If we decide that this is a case of "doctor, it hurts when I do that." / "okay, don't do that." ...
... then an easy fix would be to add the line
#![crate_name="cross_crate_spans_test"]to the file cross-crate-spans.rs
However, the specifics behind how this particular bug arises makes me wonder if it is worth a little investigation, to figure out if there is something deeper wrong here than just a "user error"
(Plus, I would probably want to file a separate bug to have the compiler say something (i.e. warn) if you are compiling a crate named
A, and yet one of its (potentially indirect) external dependencies is also a crate namedA... I suppose in principle we might support such a setup, but it seems really goofy...)- I know we support linking to multiple crates of the same name; I've advertised the fact that a single program can link to two libraries that each use independent (incompatible) versions of some crate.
- But having duplicate names on independent subtrees in your dependency graph seems quite a bit different than having an ancestor that duplicates your name.
- Anyway, I bet by even bringing up the oddity of the example, I'm undermining any motivation to fix the bug itself, which does seem weird and probably worth diagnosing properly...
Felix S Klock II at 2017-03-24 13:58:33
Thanks for the report, @pnkfelix! Nice detective work
:)Yes, at a first glance this looks like an issue betweenrustcbeing able to distinguish between two crates having the same name and LLDB not being able to do that. I'm sure the situation could be improved here but I'm not sure this requires immediate action.Michael Woerister at 2017-03-27 10:25:08
Visited during wg-debugging triage. I agree with the earlier assessment. This seems low priority to resolve due to the collision between crate names.
Wesley Wiser at 2022-06-20 14:32:23