Skip to content

Commit 6a9db47

Browse files
committed
Fix debug line info for macro expansions.
Macro expansions produce code tagged with debug locations that are completely different from the surrounding expressions. This wrecks havoc on debugger's ability the step over source lines. In order to have a good line stepping behavior in debugger, we overwrite debug locations of macro expansions with that of the outermost expansion site.
1 parent f883b0b commit 6a9db47

File tree

11 files changed

+297
-53
lines changed

11 files changed

+297
-53
lines changed

src/librustc/middle/region.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl CodeExtent {
237237
// (This is the special case aluded to in the
238238
// doc-comment for this method)
239239
let stmt_span = blk.stmts[r.first_statement_index as usize].span;
240-
Some(Span { lo: stmt_span.hi, ..blk.span })
240+
Some(Span { lo: stmt_span.hi, hi: blk.span.hi, expn_id: stmt_span.expn_id })
241241
}
242242
}
243243
}

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
914914
"force drop flag checks on or off"),
915915
trace_macros: bool = (false, parse_bool, [UNTRACKED],
916916
"for every macro invocation, print its name and arguments"),
917+
debug_macros: bool = (false, parse_bool, [TRACKED],
918+
"emit line numbers debug info inside macros"),
917919
enable_nonzeroing_move_hints: bool = (false, parse_bool, [TRACKED],
918920
"force nonzeroing move optimization on"),
919921
keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],

src/librustc_llvm/ffi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,11 @@ extern {
17961796
Col: c_uint)
17971797
-> DILexicalBlock;
17981798

1799+
pub fn LLVMRustDIBuilderCreateLexicalBlockFile(Builder: DIBuilderRef,
1800+
Scope: DIScope,
1801+
File: DIFile)
1802+
-> DILexicalBlock;
1803+
17991804
pub fn LLVMRustDIBuilderCreateStaticVariable(Builder: DIBuilderRef,
18001805
Context: DIScope,
18011806
Name: *const c_char,

src/librustc_trans/debuginfo/create_scope_map.rs

+40-8
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use rustc_data_structures::bitvec::BitVector;
2929
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
3030
use rustc::hir::{self, PatKind};
3131

32+
use syntax_pos::BytePos;
33+
3234
// This procedure builds the *scope map* for a given function, which maps any
3335
// given ast::NodeId in the function's AST to the correct DIScope metadata instance.
3436
//
@@ -68,11 +70,31 @@ pub fn create_scope_map(cx: &CrateContext,
6870
return scope_map;
6971
}
7072

73+
#[derive(Clone, Copy, Debug)]
74+
pub struct MirDebugScope {
75+
pub scope_metadata: DIScope,
76+
// Start and end offsets of the file to which this DIScope belongs.
77+
// These are used to quickly determine whether some span refers to the same file.
78+
pub file_start_pos: BytePos,
79+
pub file_end_pos: BytePos,
80+
}
81+
82+
impl MirDebugScope {
83+
pub fn is_valid(&self) -> bool {
84+
!self.scope_metadata.is_null()
85+
}
86+
}
87+
7188
/// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
7289
/// If debuginfo is disabled, the returned vector is empty.
73-
pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, DIScope> {
90+
pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, MirDebugScope> {
7491
let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
75-
let mut scopes = IndexVec::from_elem(ptr::null_mut(), &mir.visibility_scopes);
92+
let null_scope = MirDebugScope {
93+
scope_metadata: ptr::null_mut(),
94+
file_start_pos: BytePos(0),
95+
file_end_pos: BytePos(0)
96+
};
97+
let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes);
7698

7799
let fn_metadata = match fcx.debug_context {
78100
FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
@@ -102,8 +124,8 @@ fn make_mir_scope(ccx: &CrateContext,
102124
has_variables: &BitVector,
103125
fn_metadata: DISubprogram,
104126
scope: VisibilityScope,
105-
scopes: &mut IndexVec<VisibilityScope, DIScope>) {
106-
if !scopes[scope].is_null() {
127+
scopes: &mut IndexVec<VisibilityScope, MirDebugScope>) {
128+
if scopes[scope].is_valid() {
107129
return;
108130
}
109131

@@ -113,7 +135,12 @@ fn make_mir_scope(ccx: &CrateContext,
113135
scopes[parent]
114136
} else {
115137
// The root is the function itself.
116-
scopes[scope] = fn_metadata;
138+
let loc = span_start(ccx, mir.span);
139+
scopes[scope] = MirDebugScope {
140+
scope_metadata: fn_metadata,
141+
file_start_pos: loc.file.start_pos,
142+
file_end_pos: loc.file.end_pos,
143+
};
117144
return;
118145
};
119146

@@ -124,22 +151,27 @@ fn make_mir_scope(ccx: &CrateContext,
124151
// However, we don't skip creating a nested scope if
125152
// our parent is the root, because we might want to
126153
// put arguments in the root and not have shadowing.
127-
if parent_scope != fn_metadata {
154+
if parent_scope.scope_metadata != fn_metadata {
128155
scopes[scope] = parent_scope;
129156
return;
130157
}
131158
}
132159

133160
let loc = span_start(ccx, scope_data.span);
134-
scopes[scope] = unsafe {
135161
let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path);
162+
let scope_metadata = unsafe {
136163
llvm::LLVMRustDIBuilderCreateLexicalBlock(
137164
DIB(ccx),
138-
parent_scope,
165+
parent_scope.scope_metadata,
139166
file_metadata,
140167
loc.line as c_uint,
141168
loc.col.to_usize() as c_uint)
142169
};
170+
scopes[scope] = MirDebugScope {
171+
scope_metadata: scope_metadata,
172+
file_start_pos: loc.file.start_pos,
173+
file_end_pos: loc.file.end_pos,
174+
};
143175
}
144176

145177
// local helper functions for walking the AST.

src/librustc_trans/debuginfo/metadata.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use context::SharedCrateContext;
2323
use session::Session;
2424

2525
use llvm::{self, ValueRef};
26-
use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType};
26+
use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock};
2727

2828
use rustc::hir::def_id::DefId;
2929
use rustc::hir::pat_util;
@@ -2086,3 +2086,17 @@ pub fn create_argument_metadata(bcx: Block, arg: &hir::Arg) {
20862086
span);
20872087
})
20882088
}
2089+
2090+
// Creates an "extension" of an existing DIScope into another file.
2091+
pub fn extend_scope_to_file(ccx: &CrateContext,
2092+
scope_metadata: DIScope,
2093+
file: &syntax_pos::FileMap)
2094+
-> DILexicalBlock {
2095+
let file_metadata = file_metadata(ccx, &file.name, &file.abs_path);
2096+
unsafe {
2097+
llvm::LLVMRustDIBuilderCreateLexicalBlockFile(
2098+
DIB(ccx),
2099+
scope_metadata,
2100+
file_metadata)
2101+
}
2102+
}

src/librustc_trans/debuginfo/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub mod metadata;
5454
mod create_scope_map;
5555
mod source_loc;
5656

57-
pub use self::create_scope_map::create_mir_scopes;
57+
pub use self::create_scope_map::{create_mir_scopes, MirDebugScope};
5858
pub use self::source_loc::start_emitting_source_locations;
5959
pub use self::source_loc::get_cleanup_debug_loc_for_ast_node;
6060
pub use self::source_loc::with_source_location_override;
@@ -63,6 +63,7 @@ pub use self::metadata::create_argument_metadata;
6363
pub use self::metadata::create_captured_var_metadata;
6464
pub use self::metadata::create_global_var_metadata;
6565
pub use self::metadata::create_local_var_metadata;
66+
pub use self::metadata::extend_scope_to_file;
6667

6768
#[allow(non_upper_case_globals)]
6869
const DW_TAG_auto_variable: c_uint = 0x100;

src/librustc_trans/mir/mod.rs

+100-41
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,17 @@
1010

1111
use libc::c_uint;
1212
use llvm::{self, ValueRef};
13-
use llvm::debuginfo::DIScope;
1413
use rustc::ty;
1514
use rustc::mir::repr as mir;
1615
use rustc::mir::tcx::LvalueTy;
1716
use session::config::FullDebugInfo;
1817
use base;
1918
use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null};
20-
use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind};
19+
use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind, FunctionDebugContext};
2120
use machine;
2221
use type_of;
2322

24-
use syntax_pos::DUMMY_SP;
23+
use syntax_pos::{DUMMY_SP, NO_EXPANSION, COMMAND_LINE_EXPN, BytePos};
2524
use syntax::parse::token::keywords;
2625

2726
use std::ops::Deref;
@@ -103,12 +102,67 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
103102
locals: IndexVec<mir::Local, LocalRef<'tcx>>,
104103

105104
/// Debug information for MIR scopes.
106-
scopes: IndexVec<mir::VisibilityScope, DIScope>
105+
scopes: IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>,
107106
}
108107

109108
impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
110-
pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc {
111-
DebugLoc::ScopeAt(self.scopes[source_info.scope], source_info.span)
109+
pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> DebugLoc {
110+
// Bail out if debug info emission is not enabled.
111+
match self.fcx.debug_context {
112+
FunctionDebugContext::DebugInfoDisabled |
113+
FunctionDebugContext::FunctionWithoutDebugInfo => {
114+
// Can't return DebugLoc::None here because intrinsic::trans_intrinsic_call()
115+
// relies on debug location to obtain span of the call site.
116+
return DebugLoc::ScopeAt(self.scopes[source_info.scope].scope_metadata,
117+
source_info.span);
118+
}
119+
FunctionDebugContext::RegularContext(_) =>{}
120+
}
121+
122+
// In order to have a good line stepping behavior in debugger, we overwrite debug
123+
// locations of macro expansions with that of the outermost expansion site
124+
// (unless the crate is being compiled with `-Z debug-macros`).
125+
if source_info.span.expn_id == NO_EXPANSION ||
126+
source_info.span.expn_id == COMMAND_LINE_EXPN ||
127+
self.fcx.ccx.sess().opts.debugging_opts.debug_macros {
128+
129+
let scope_metadata = self.scope_metadata_for_loc(source_info.scope,
130+
source_info.span.lo);
131+
DebugLoc::ScopeAt(scope_metadata, source_info.span)
132+
} else {
133+
let cm = self.fcx.ccx.sess().codemap();
134+
// Walk up the macro expansion chain until we reach a non-expanded span.
135+
let mut span = source_info.span;
136+
while span.expn_id != NO_EXPANSION && span.expn_id != COMMAND_LINE_EXPN {
137+
if let Some(callsite_span) = cm.with_expn_info(span.expn_id,
138+
|ei| ei.map(|ei| ei.call_site.clone())) {
139+
span = callsite_span;
140+
} else {
141+
break;
142+
}
143+
}
144+
let scope_metadata = self.scope_metadata_for_loc(source_info.scope, span.lo);
145+
// Use span of the outermost call site, while keeping the original lexical scope
146+
DebugLoc::ScopeAt(scope_metadata, span)
147+
}
148+
}
149+
150+
// DILocations inherit source file name from the parent DIScope. Due to macro expansions
151+
// it may so happen that the current span belongs to a different file than the DIScope
152+
// corresponding to span's containing visibility scope. If so, we need to create a DIScope
153+
// "extension" into that file.
154+
fn scope_metadata_for_loc(&self, scope_id: mir::VisibilityScope, pos: BytePos)
155+
-> llvm::debuginfo::DIScope {
156+
let scope_metadata = self.scopes[scope_id].scope_metadata;
157+
if pos < self.scopes[scope_id].file_start_pos ||
158+
pos >= self.scopes[scope_id].file_end_pos {
159+
let cm = self.fcx.ccx.sess().codemap();
160+
debuginfo::extend_scope_to_file(self.fcx.ccx,
161+
scope_metadata,
162+
&cm.lookup_char_pos(pos).file)
163+
} else {
164+
scope_metadata
165+
}
112166
}
113167
}
114168

@@ -155,16 +209,38 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
155209
analyze::cleanup_kinds(bcx, &mir))
156210
});
157211

212+
// Allocate a `Block` for every basic block
213+
let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> =
214+
mir.basic_blocks().indices().map(|bb| {
215+
if bb == mir::START_BLOCK {
216+
fcx.new_block("start", None)
217+
} else {
218+
fcx.new_block(&format!("{:?}", bb), None)
219+
}
220+
}).collect();
221+
158222
// Compute debuginfo scopes from MIR scopes.
159223
let scopes = debuginfo::create_mir_scopes(fcx);
160224

225+
let mut mircx = MirContext {
226+
mir: mir.clone(),
227+
fcx: fcx,
228+
llpersonalityslot: None,
229+
blocks: block_bcxs,
230+
unreachable_block: None,
231+
cleanup_kinds: cleanup_kinds,
232+
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
233+
scopes: scopes,
234+
locals: IndexVec::new(),
235+
};
236+
161237
// Allocate variable and temp allocas
162-
let locals = {
163-
let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals);
238+
mircx.locals = {
239+
let args = arg_local_refs(&bcx, &mir, &mircx.scopes, &lvalue_locals);
164240
let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| {
165241
let ty = bcx.monomorphize(&decl.ty);
166-
let scope = scopes[decl.source_info.scope];
167-
let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo;
242+
let debug_scope = mircx.scopes[decl.source_info.scope];
243+
let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo;
168244

169245
let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap();
170246
if !lvalue_locals.contains(local.index()) && !dbg {
@@ -173,11 +249,16 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
173249

174250
let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str());
175251
if dbg {
176-
bcx.with_block(|bcx| {
177-
declare_local(bcx, decl.name, ty, scope,
178-
VariableAccess::DirectVariable { alloca: lvalue.llval },
179-
VariableKind::LocalVariable, decl.source_info.span);
180-
});
252+
let dbg_loc = mircx.debug_loc(decl.source_info);
253+
if let DebugLoc::ScopeAt(scope, span) = dbg_loc {
254+
bcx.with_block(|bcx| {
255+
declare_local(bcx, decl.name, ty, scope,
256+
VariableAccess::DirectVariable { alloca: lvalue.llval },
257+
VariableKind::LocalVariable, span);
258+
});
259+
} else {
260+
panic!("Unexpected");
261+
}
181262
}
182263
LocalRef::Lvalue(lvalue)
183264
});
@@ -203,37 +284,15 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
203284
})).collect()
204285
};
205286

206-
// Allocate a `Block` for every basic block
207-
let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> =
208-
mir.basic_blocks().indices().map(|bb| {
209-
if bb == mir::START_BLOCK {
210-
fcx.new_block("start", None)
211-
} else {
212-
fcx.new_block(&format!("{:?}", bb), None)
213-
}
214-
}).collect();
215-
216287
// Branch to the START block
217-
let start_bcx = block_bcxs[mir::START_BLOCK];
288+
let start_bcx = mircx.blocks[mir::START_BLOCK];
218289
bcx.br(start_bcx.llbb);
219290

220291
// Up until here, IR instructions for this function have explicitly not been annotated with
221292
// source code location, so we don't step into call setup code. From here on, source location
222293
// emitting should be enabled.
223294
debuginfo::start_emitting_source_locations(fcx);
224295

225-
let mut mircx = MirContext {
226-
mir: mir.clone(),
227-
fcx: fcx,
228-
llpersonalityslot: None,
229-
blocks: block_bcxs,
230-
unreachable_block: None,
231-
cleanup_kinds: cleanup_kinds,
232-
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
233-
locals: locals,
234-
scopes: scopes
235-
};
236-
237296
let mut visited = BitVector::new(mir.basic_blocks().len());
238297

239298
let mut rpo = traversal::reverse_postorder(&mir);
@@ -271,7 +330,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
271330
/// indirect.
272331
fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
273332
mir: &mir::Mir<'tcx>,
274-
scopes: &IndexVec<mir::VisibilityScope, DIScope>,
333+
scopes: &IndexVec<mir::VisibilityScope, debuginfo::MirDebugScope>,
275334
lvalue_locals: &BitVector)
276335
-> Vec<LocalRef<'tcx>> {
277336
let fcx = bcx.fcx();
@@ -281,8 +340,8 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
281340

282341
// Get the argument scope, if it exists and if we need it.
283342
let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE];
284-
let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
285-
Some(arg_scope)
343+
let arg_scope = if arg_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo {
344+
Some(arg_scope.scope_metadata)
286345
} else {
287346
None
288347
};

0 commit comments

Comments
 (0)