diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 6f0ad087dc589..faf2f7dae08c5 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -237,7 +237,7 @@ impl CodeExtent { // (This is the special case aluded to in the // doc-comment for this method) let stmt_span = blk.stmts[r.first_statement_index as usize].span; - Some(Span { lo: stmt_span.hi, ..blk.span }) + Some(Span { lo: stmt_span.hi, hi: blk.span.hi, expn_id: stmt_span.expn_id }) } } } diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index 6301c57c55540..9b089b1f82052 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -637,6 +637,8 @@ extern { pub fn LLVMAddNamedMetadataOperand(M: ModuleRef, Str: *const c_char, Val: ValueRef); + pub fn LLVMRustMetadataAsValue(context: ContextRef, metadata: MetadataRef) -> ValueRef; + pub fn LLVMRustValueAsMetadata(value: ValueRef) -> MetadataRef; /* Operations on scalar constants */ pub fn LLVMConstInt(IntTy: TypeRef, N: c_ulonglong, SignExtend: Bool) @@ -1796,6 +1798,11 @@ extern { Col: c_uint) -> DILexicalBlock; + pub fn LLVMRustDIBuilderCreateLexicalBlockFile(Builder: DIBuilderRef, + Scope: DIScope, + File: DIFile) + -> DILexicalBlock; + pub fn LLVMRustDIBuilderCreateStaticVariable(Builder: DIBuilderRef, Context: DIScope, Name: *const c_char, diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs index fe6a48d4c559d..7947ff7c8a986 100644 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/debuginfo/create_scope_map.rs @@ -29,6 +29,8 @@ use rustc_data_structures::bitvec::BitVector; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::hir::{self, PatKind}; +use syntax_pos::BytePos; + // This procedure builds the *scope map* for a given function, which maps any // given ast::NodeId in the function's AST to the correct DIScope metadata instance. // @@ -68,11 +70,29 @@ pub fn create_scope_map(cx: &CrateContext, return scope_map; } +#[derive(Clone, Copy, Debug)] +pub struct MirDebugScope { + pub scope_metadata: DIScope, + pub start_pos: BytePos, + pub end_pos: BytePos, +} + +impl MirDebugScope { + pub fn is_valid(&self) -> bool { + !self.scope_metadata.is_null() + } +} + /// Produce DIScope DIEs for each MIR Scope which has variables defined in it. /// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec { +pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec { let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn"); - let mut scopes = IndexVec::from_elem(ptr::null_mut(), &mir.visibility_scopes); + let null_scope = MirDebugScope { + scope_metadata: ptr::null_mut(), + start_pos: BytePos(0), + end_pos: BytePos(0) + }; + let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); let fn_metadata = match fcx.debug_context { FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata, @@ -102,8 +122,8 @@ fn make_mir_scope(ccx: &CrateContext, has_variables: &BitVector, fn_metadata: DISubprogram, scope: VisibilityScope, - scopes: &mut IndexVec) { - if !scopes[scope].is_null() { + scopes: &mut IndexVec) { + if scopes[scope].is_valid() { return; } @@ -113,7 +133,12 @@ fn make_mir_scope(ccx: &CrateContext, scopes[parent] } else { // The root is the function itself. - scopes[scope] = fn_metadata; + let loc = span_start(ccx, mir.span); + scopes[scope] = MirDebugScope { + scope_metadata: fn_metadata, + start_pos: loc.file.start_pos, + end_pos: loc.file.end_pos, + }; return; }; @@ -124,22 +149,27 @@ fn make_mir_scope(ccx: &CrateContext, // However, we don't skip creating a nested scope if // our parent is the root, because we might want to // put arguments in the root and not have shadowing. - if parent_scope != fn_metadata { + if parent_scope.scope_metadata != fn_metadata { scopes[scope] = parent_scope; return; } } let loc = span_start(ccx, scope_data.span); - scopes[scope] = unsafe { let file_metadata = file_metadata(ccx, &loc.file.name, &loc.file.abs_path); + let scope_metadata = unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlock( DIB(ccx), - parent_scope, + parent_scope.scope_metadata, file_metadata, loc.line as c_uint, loc.col.to_usize() as c_uint) }; + scopes[scope] = MirDebugScope { + scope_metadata: scope_metadata, + start_pos: loc.file.start_pos, + end_pos: loc.file.end_pos, + }; } // local helper functions for walking the AST. diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 8011347d3eb12..50107ce6b4b50 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -23,7 +23,7 @@ use context::SharedCrateContext; use session::Session; use llvm::{self, ValueRef}; -use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType}; +use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, DICompositeType, DILexicalBlock}; use rustc::hir::def_id::DefId; use rustc::hir::pat_util; @@ -2104,3 +2104,17 @@ pub fn create_argument_metadata(bcx: Block, arg: &hir::Arg) { span); }) } + +// Creates an "extension" of an existing DIScope into another file. +pub fn extend_scope_to_file(ccx: &CrateContext, + scope_metadata: DIScope, + file: &syntax_pos::FileMap) + -> DILexicalBlock { + let file_metadata = file_metadata(ccx, &file.name, &file.abs_path); + unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlockFile( + DIB(ccx), + scope_metadata, + file_metadata) + } +} diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs index 464c32c3cc725..7a79e2de713a0 100644 --- a/src/librustc_trans/debuginfo/mod.rs +++ b/src/librustc_trans/debuginfo/mod.rs @@ -43,7 +43,7 @@ use std::cell::{Cell, RefCell}; use std::ffi::CString; use std::ptr; -use syntax_pos::{self, Span, Pos}; +use syntax_pos::{self, Span}; use syntax::ast; use syntax::attr::IntType; @@ -55,7 +55,7 @@ pub mod metadata; mod create_scope_map; mod source_loc; -pub use self::create_scope_map::create_mir_scopes; +pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; pub use self::source_loc::start_emitting_source_locations; pub use self::source_loc::get_cleanup_debug_loc_for_ast_node; pub use self::source_loc::with_source_location_override; @@ -64,6 +64,7 @@ pub use self::metadata::create_argument_metadata; pub use self::metadata::create_captured_var_metadata; pub use self::metadata::create_global_var_metadata; pub use self::metadata::create_local_var_metadata; +pub use self::metadata::extend_scope_to_file; #[allow(non_upper_case_globals)] const DW_TAG_auto_variable: c_uint = 0x100; @@ -509,7 +510,7 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, argument_index) }; source_loc::set_debug_location(cx, None, - InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); + InternalDebugLocation::KnownLocation(scope_metadata, span.lo, None)); unsafe { let debug_loc = llvm::LLVMGetCurrentDebugLocation(cx.raw_builder()); let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( @@ -542,7 +543,7 @@ pub fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DebugLoc { At(ast::NodeId, Span), - ScopeAt(DIScope, Span), + ScopeAt(DIScope, Span, Option<(DIScope, Span)>), None } diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs index d288b9dcef70b..2789120bdc049 100644 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ b/src/librustc_trans/debuginfo/source_loc.rs @@ -10,7 +10,7 @@ use self::InternalDebugLocation::*; -use super::utils::{debug_context, span_start}; +use super::utils::debug_context; use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER}; use super::{FunctionDebugContext, DebugLoc}; @@ -21,7 +21,7 @@ use common::{NodeIdAndSpan, CrateContext, FunctionContext}; use libc::c_uint; use std::ptr; -use syntax_pos::{self, Span, Pos}; +use syntax_pos::{self, Span, BytePos}; use syntax::ast; pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -108,21 +108,20 @@ pub fn set_source_location(fcx: &FunctionContext, } let dbg_loc = if function_debug_context.source_locations_enabled.get() { - let (scope, span) = match debug_loc { + match debug_loc { DebugLoc::At(node_id, span) => { - (scope_metadata(fcx, node_id, span), span) + let scope = scope_metadata(fcx, node_id, span); + KnownLocation(scope, span.lo, None) } - DebugLoc::ScopeAt(scope, span) => (scope, span), - DebugLoc::None => { - set_debug_location(fcx.ccx, builder, UnknownLocation); - return; + DebugLoc::ScopeAt(scope, span, inlined_at) => { + let inlined_at_loc = match inlined_at { + Some((scope, span)) => Some((scope, span.lo)), + None => None + }; + KnownLocation(scope, span.lo, inlined_at_loc) } - }; - - debug!("set_source_location: {}", - fcx.ccx.sess().codemap().span_to_string(span)); - let loc = span_start(fcx.ccx, span); - InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) + DebugLoc::None => UnknownLocation + } } else { UnknownLocation }; @@ -173,23 +172,12 @@ pub fn start_emitting_source_locations(fcx: &FunctionContext) { } } - #[derive(Copy, Clone, PartialEq)] pub enum InternalDebugLocation { - KnownLocation { scope: DIScope, line: usize, col: usize }, + KnownLocation(DIScope, BytePos, Option<(DIScope, BytePos)>), UnknownLocation } -impl InternalDebugLocation { - pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { - KnownLocation { - scope: scope, - line: line, - col: col, - } - } -} - pub fn set_debug_location(cx: &CrateContext, builder: Option, debug_location: InternalDebugLocation) { @@ -199,19 +187,37 @@ pub fn set_debug_location(cx: &CrateContext, } } + let cm = cx.sess().codemap(); let metadata_node = match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - + KnownLocation(scope, pos, inlined_at) => { + + let inlined_at_loc = match inlined_at { + Some((scope, pos)) => { + let loc = cm.lookup_char_pos(pos); + unsafe { + llvm::LLVMRustValueAsMetadata( + llvm::LLVMRustDIBuilderCreateDebugLocation( + debug_context(cx).llcontext, + loc.line as c_uint, + UNKNOWN_COLUMN_NUMBER as c_uint, + scope, + ptr::null_mut()) + ) + } + }, + None => ptr::null_mut() + }; + + let loc = cm.lookup_char_pos(pos); + debug!("setting debug location to line {}", loc.line); + // Set the column to zero like Clang and GCC unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation( debug_context(cx).llcontext, - line as c_uint, - col as c_uint, + loc.line as c_uint, + UNKNOWN_COLUMN_NUMBER as c_uint, scope, - ptr::null_mut()) + inlined_at_loc) } } UnknownLocation => { diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 2f27aed065d80..0206aa5797187 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -121,7 +121,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let name = tcx.item_name(def_id).as_str(); let span = match call_debug_location { - DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span) => span, + DebugLoc::At(_, span) | DebugLoc::ScopeAt(_, span, _) => span, DebugLoc::None => { span_bug!(fcx.span.unwrap_or(DUMMY_SP), "intrinsic `{}` called with missing span", name); diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 8f723d288c9eb..eebadc50a2046 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -10,18 +10,17 @@ use libc::c_uint; use llvm::{self, ValueRef}; -use llvm::debuginfo::DIScope; use rustc::ty; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; use session::config::FullDebugInfo; use base; use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext, C_null}; -use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind}; +use debuginfo::{self, declare_local, DebugLoc, VariableAccess, VariableKind, FunctionDebugContext}; use machine; use type_of; -use syntax_pos::DUMMY_SP; +use syntax_pos::{DUMMY_SP, NO_EXPANSION, Span}; use syntax::parse::token::keywords; use std::ops::Deref; @@ -102,12 +101,64 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { locals: IndexVec>, /// Debug information for MIR scopes. - scopes: IndexVec + scopes: IndexVec, } impl<'blk, 'tcx> MirContext<'blk, 'tcx> { - pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc { - DebugLoc::ScopeAt(self.scopes[source_info.scope], source_info.span) + pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> DebugLoc { + match self.fcx.debug_context { + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + // Can't return DebugLoc::None here because intrinsic::trans_intrinsic_call() + // relies on debug location to obtain span of the call site. + return DebugLoc::ScopeAt(self.scopes[source_info.scope].scope_metadata, + source_info.span, None); + } + FunctionDebugContext::RegularContext(_) =>{} + } + + if source_info.span.expn_id == NO_EXPANSION { + let scope_metadata = self.scope_metadata_for_span(source_info.scope, source_info.span); + DebugLoc::ScopeAt(scope_metadata, source_info.span, None) + } else { + // If current span is a result of a macro expansion, we walk up the expansion chain + // till we reach the outermost expansion site. + let mut scope_id = source_info.scope; + let mut span = source_info.span; + let cm = self.fcx.ccx.sess().codemap(); + while span.expn_id != NO_EXPANSION { + span = cm.source_callsite(span); + } + // Ditto for scope + while self.mir.visibility_scopes[scope_id].span.expn_id != NO_EXPANSION { + let parent_scope_id = self.mir.visibility_scopes[scope_id].parent_scope; + scope_id = match parent_scope_id { + Some(id) => id, + None => break // Can happen if the current function is defined inside a macro. + } + } + let scope_metadata = self.scope_metadata_for_span(source_info.scope, source_info.span); + let inlined_at = (self.scope_metadata_for_span(scope_id, span), span); + DebugLoc::ScopeAt(scope_metadata, source_info.span, Some(inlined_at)) + } + } + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing visibility scope. If so, we need to create a DIScope + // "extension" into that file. + fn scope_metadata_for_span(&self, scope_id: mir::VisibilityScope, span: Span) + -> llvm::debuginfo::DIScope { + let scope_metadata = self.scopes[scope_id].scope_metadata; + if span.lo < self.scopes[scope_id].start_pos || + span.lo > self.scopes[scope_id].end_pos { + let cm = self.fcx.ccx.sess().codemap(); + debuginfo::extend_scope_to_file(self.fcx.ccx, + scope_metadata, + &cm.lookup_char_pos(span.lo).file) + } else { + scope_metadata + } } } @@ -162,8 +213,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let args = arg_local_refs(&bcx, &mir, &scopes, &lvalue_locals); let vars = mir.var_decls.iter().enumerate().map(|(i, decl)| { let ty = bcx.monomorphize(&decl.ty); - let scope = scopes[decl.source_info.scope]; - let dbg = !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo; + let debug_scope = scopes[decl.source_info.scope]; + let dbg = debug_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo; let local = mir.local_index(&mir::Lvalue::Var(mir::Var::new(i))).unwrap(); if !lvalue_locals.contains(local.index()) && !dbg { @@ -173,7 +224,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let lvalue = LvalueRef::alloca(&bcx, ty, &decl.name.as_str()); if dbg { bcx.with_block(|bcx| { - declare_local(bcx, decl.name, ty, scope, + declare_local(bcx, decl.name, ty, debug_scope.scope_metadata, VariableAccess::DirectVariable { alloca: lvalue.llval }, VariableKind::LocalVariable, decl.source_info.span); }); @@ -230,7 +281,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { cleanup_kinds: cleanup_kinds, landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), locals: locals, - scopes: scopes + scopes: scopes, }; let mut visited = BitVector::new(mir.basic_blocks().len()); @@ -270,7 +321,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { /// indirect. fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mir: &mir::Mir<'tcx>, - scopes: &IndexVec, + scopes: &IndexVec, lvalue_locals: &BitVector) -> Vec> { let fcx = bcx.fcx(); @@ -280,8 +331,8 @@ fn arg_local_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, // Get the argument scope, if it exists and if we need it. let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; - let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo { - Some(arg_scope) + let arg_scope = if arg_scope.is_valid() && bcx.sess().opts.debuginfo == FullDebugInfo { + Some(arg_scope.scope_metadata) } else { None }; diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 0da25e7ac57b7..3dd2a730d3a40 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -521,6 +521,15 @@ extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateLexicalBlock( )); } +extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( + LLVMRustDIBuilderRef Builder, + LLVMRustMetadataRef Scope, + LLVMRustMetadataRef File) { + return wrap(Builder->createLexicalBlockFile( + unwrapDI(Scope), + unwrapDI(File))); +} + extern "C" LLVMRustMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMRustDIBuilderRef Builder, LLVMRustMetadataRef Context, @@ -1223,3 +1232,18 @@ extern "C" void LLVMRustUnsetComdat(LLVMValueRef V) { GlobalObject *GV = unwrap(V); GV->setComdat(nullptr); } + +extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef context, + LLVMRustMetadataRef metadata) { + if (metadata) + return wrap(MetadataAsValue::get(*unwrap(context), unwrapDIptr(metadata))); + else + return NULL; +} + +extern "C" LLVMRustMetadataRef LLVMRustValueAsMetadata(LLVMValueRef value) { + if (value) + return wrap(cast(unwrap(value)->getMetadata())); + else + return NULL; +} diff --git a/src/test/debuginfo/macro-stepping.rs b/src/test/debuginfo/macro-stepping.rs new file mode 100644 index 0000000000000..3fb4d3d7bf7cc --- /dev/null +++ b/src/test/debuginfo/macro-stepping.rs @@ -0,0 +1,91 @@ +// Copyright 2013-2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-windows +// min-lldb-version: 310 + +// compile-flags:-g -Zorbit + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc1[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc2[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc3[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc4[...] +// gdb-command:next +// gdb-command:f +// gdb-check:[...]#loc5[...] + +// === LLDB TESTS ================================================================================== + +// lldb-command:set set stop-line-count-before 0 +// lldb-command:set set stop-line-count-after 1 +// Can't set both to zero or lldb will stop printing source at all. So it will output the current +// line and the next. We deal with this by having at least 2 lines between the #loc's + +// lldb-command:run +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc1[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc2[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc3[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc4[...] +// lldb-command:next +// lldb-command:f +// lldb-check:[...]#loc5[...] + +#![allow(unused)] + +macro_rules! foo { + () => { + let a = 1; + let b = 2; + let c = 3; + } +} + +macro_rules! foo2 { + () => { + foo!(); + let x = 1; + foo!(); + } +} + +fn main() { + zzz(); // #break + + foo!(); // #loc1 + + foo2!(); // #loc2 + + let x = vec![42]; // #loc3 + + println!("Hello {}", "world"); // #loc4 + + zzz(); // #loc5 +} + +fn zzz() {()}