rollup merge of #24762: nrc/mod-debug-2

Closes #20780

r? @michaelwoerister

I'm sure this could be done better with deeper knowledge of debuginfo, but this seems like a good start.
This commit is contained in:
Alex Crichton 2015-04-29 15:45:35 -07:00
commit 3434469b51
10 changed files with 2918 additions and 2728 deletions

View File

@ -2182,7 +2182,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
unsafe {
llvm::LLVMPositionBuilderAtEnd(bld, llbb);
debuginfo::insert_reference_to_gdb_debug_scripts_section_global(ccx);
debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(ccx);
let (start_fn, args) = if use_start_lang_item {
let start_def_id = match ccx.tcx().lang_items.require(StartFnLangItem) {

View File

@ -0,0 +1,514 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::metadata::file_metadata;
use super::utils::DIB;
use llvm;
use llvm::debuginfo::{DIScope, DISubprogram};
use trans::common::CrateContext;
use middle::pat_util;
use util::nodemap::NodeMap;
use libc::c_uint;
use syntax::codemap::{Span, Pos};
use syntax::{ast, codemap, ast_util};
// 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.
//
// This builder procedure walks the AST in execution order and keeps track of
// what belongs to which scope, creating DIScope DIEs along the way, and
// introducing *artificial* lexical scope descriptors where necessary. These
// artificial scopes allow GDB to correctly handle name shadowing.
pub fn create_scope_map(cx: &CrateContext,
args: &[ast::Arg],
fn_entry_block: &ast::Block,
fn_metadata: DISubprogram,
fn_ast_id: ast::NodeId)
-> NodeMap<DIScope> {
let mut scope_map = NodeMap();
let def_map = &cx.tcx().def_map;
let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata, name: None });
scope_map.insert(fn_ast_id, fn_metadata);
// Push argument identifiers onto the stack so arguments integrate nicely
// with variable shadowing.
for arg in args {
pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| {
scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata,
name: Some(path1.node.name) });
scope_map.insert(node_id, fn_metadata);
})
}
// Clang creates a separate scope for function bodies, so let's do this too.
with_new_scope(cx,
fn_entry_block.span,
&mut scope_stack,
&mut scope_map,
|cx, scope_stack, scope_map| {
walk_block(cx, fn_entry_block, scope_stack, scope_map);
});
return scope_map;
}
// local helper functions for walking the AST.
fn with_new_scope<F>(cx: &CrateContext,
scope_span: Span,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>,
inner_walk: F) where
F: FnOnce(&CrateContext, &mut Vec<ScopeStackEntry>, &mut NodeMap<DIScope>),
{
// Create a new lexical scope and push it onto the stack
let loc = cx.sess().codemap().lookup_char_pos(scope_span.lo);
let file_metadata = file_metadata(cx, &loc.file.name);
let parent_scope = scope_stack.last().unwrap().scope_metadata;
let scope_metadata = unsafe {
llvm::LLVMDIBuilderCreateLexicalBlock(
DIB(cx),
parent_scope,
file_metadata,
loc.line as c_uint,
loc.col.to_usize() as c_uint)
};
scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, name: None });
inner_walk(cx, scope_stack, scope_map);
// pop artificial scopes
while scope_stack.last().unwrap().name.is_some() {
scope_stack.pop();
}
if scope_stack.last().unwrap().scope_metadata != scope_metadata {
cx.sess().span_bug(scope_span, "debuginfo: Inconsistency in scope management.");
}
scope_stack.pop();
}
struct ScopeStackEntry {
scope_metadata: DIScope,
name: Option<ast::Name>
}
fn walk_block(cx: &CrateContext,
block: &ast::Block,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
scope_map.insert(block.id, scope_stack.last().unwrap().scope_metadata);
// The interesting things here are statements and the concluding expression.
for statement in &block.stmts {
scope_map.insert(ast_util::stmt_id(&**statement),
scope_stack.last().unwrap().scope_metadata);
match statement.node {
ast::StmtDecl(ref decl, _) =>
walk_decl(cx, &**decl, scope_stack, scope_map),
ast::StmtExpr(ref exp, _) |
ast::StmtSemi(ref exp, _) =>
walk_expr(cx, &**exp, scope_stack, scope_map),
ast::StmtMac(..) => () // Ignore macros (which should be expanded anyway).
}
}
if let Some(ref exp) = block.expr {
walk_expr(cx, &**exp, scope_stack, scope_map);
}
}
fn walk_decl(cx: &CrateContext,
decl: &ast::Decl,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
match *decl {
codemap::Spanned { node: ast::DeclLocal(ref local), .. } => {
scope_map.insert(local.id, scope_stack.last().unwrap().scope_metadata);
walk_pattern(cx, &*local.pat, scope_stack, scope_map);
if let Some(ref exp) = local.init {
walk_expr(cx, &**exp, scope_stack, scope_map);
}
}
_ => ()
}
}
fn walk_pattern(cx: &CrateContext,
pat: &ast::Pat,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
let def_map = &cx.tcx().def_map;
// Unfortunately, we cannot just use pat_util::pat_bindings() or
// ast_util::walk_pat() here because we have to visit *all* nodes in
// order to put them into the scope map. The above functions don't do that.
match pat.node {
ast::PatIdent(_, ref path1, ref sub_pat_opt) => {
// Check if this is a binding. If so we need to put it on the
// scope stack and maybe introduce an artificial scope
if pat_util::pat_is_binding(def_map, &*pat) {
let name = path1.node.name;
// LLVM does not properly generate 'DW_AT_start_scope' fields
// for variable DIEs. For this reason we have to introduce
// an artificial scope at bindings whenever a variable with
// the same name is declared in *any* parent scope.
//
// Otherwise the following error occurs:
//
// let x = 10;
//
// do_something(); // 'gdb print x' correctly prints 10
//
// {
// do_something(); // 'gdb print x' prints 0, because it
// // already reads the uninitialized 'x'
// // from the next line...
// let x = 100;
// do_something(); // 'gdb print x' correctly prints 100
// }
// Is there already a binding with that name?
// N.B.: this comparison must be UNhygienic... because
// gdb knows nothing about the context, so any two
// variables with the same name will cause the problem.
let need_new_scope = scope_stack
.iter()
.any(|entry| entry.name == Some(name));
if need_new_scope {
// Create a new lexical scope and push it onto the stack
let loc = cx.sess().codemap().lookup_char_pos(pat.span.lo);
let file_metadata = file_metadata(cx, &loc.file.name);
let parent_scope = scope_stack.last().unwrap().scope_metadata;
let scope_metadata = unsafe {
llvm::LLVMDIBuilderCreateLexicalBlock(
DIB(cx),
parent_scope,
file_metadata,
loc.line as c_uint,
loc.col.to_usize() as c_uint)
};
scope_stack.push(ScopeStackEntry {
scope_metadata: scope_metadata,
name: Some(name)
});
} else {
// Push a new entry anyway so the name can be found
let prev_metadata = scope_stack.last().unwrap().scope_metadata;
scope_stack.push(ScopeStackEntry {
scope_metadata: prev_metadata,
name: Some(name)
});
}
}
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
if let Some(ref sub_pat) = *sub_pat_opt {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
}
ast::PatWild(_) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
}
ast::PatEnum(_, ref sub_pats_opt) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
if let Some(ref sub_pats) = *sub_pats_opt {
for p in sub_pats {
walk_pattern(cx, &**p, scope_stack, scope_map);
}
}
}
ast::PatQPath(..) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
}
ast::PatStruct(_, ref field_pats, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for &codemap::Spanned {
node: ast::FieldPat { pat: ref sub_pat, .. },
..
} in field_pats.iter() {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
}
ast::PatTup(ref sub_pats) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for sub_pat in sub_pats {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
}
ast::PatBox(ref sub_pat) | ast::PatRegion(ref sub_pat, _) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
ast::PatLit(ref exp) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
walk_expr(cx, &**exp, scope_stack, scope_map);
}
ast::PatRange(ref exp1, ref exp2) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
walk_expr(cx, &**exp1, scope_stack, scope_map);
walk_expr(cx, &**exp2, scope_stack, scope_map);
}
ast::PatVec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => {
scope_map.insert(pat.id, scope_stack.last().unwrap().scope_metadata);
for sub_pat in front_sub_pats {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
if let Some(ref sub_pat) = *middle_sub_pats {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
for sub_pat in back_sub_pats {
walk_pattern(cx, &**sub_pat, scope_stack, scope_map);
}
}
ast::PatMac(_) => {
cx.sess().span_bug(pat.span, "debuginfo::create_scope_map() - \
Found unexpanded macro.");
}
}
}
fn walk_expr(cx: &CrateContext,
exp: &ast::Expr,
scope_stack: &mut Vec<ScopeStackEntry> ,
scope_map: &mut NodeMap<DIScope>) {
scope_map.insert(exp.id, scope_stack.last().unwrap().scope_metadata);
match exp.node {
ast::ExprLit(_) |
ast::ExprBreak(_) |
ast::ExprAgain(_) |
ast::ExprPath(..) => {}
ast::ExprCast(ref sub_exp, _) |
ast::ExprAddrOf(_, ref sub_exp) |
ast::ExprField(ref sub_exp, _) |
ast::ExprTupField(ref sub_exp, _) |
ast::ExprParen(ref sub_exp) =>
walk_expr(cx, &**sub_exp, scope_stack, scope_map),
ast::ExprBox(ref place, ref sub_expr) => {
place.as_ref().map(
|e| walk_expr(cx, &**e, scope_stack, scope_map));
walk_expr(cx, &**sub_expr, scope_stack, scope_map);
}
ast::ExprRet(ref exp_opt) => match *exp_opt {
Some(ref sub_exp) => walk_expr(cx, &**sub_exp, scope_stack, scope_map),
None => ()
},
ast::ExprUnary(_, ref sub_exp) => {
walk_expr(cx, &**sub_exp, scope_stack, scope_map);
}
ast::ExprAssignOp(_, ref lhs, ref rhs) |
ast::ExprIndex(ref lhs, ref rhs) |
ast::ExprBinary(_, ref lhs, ref rhs) => {
walk_expr(cx, &**lhs, scope_stack, scope_map);
walk_expr(cx, &**rhs, scope_stack, scope_map);
}
ast::ExprRange(ref start, ref end) => {
start.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
end.as_ref().map(|e| walk_expr(cx, &**e, scope_stack, scope_map));
}
ast::ExprVec(ref init_expressions) |
ast::ExprTup(ref init_expressions) => {
for ie in init_expressions {
walk_expr(cx, &**ie, scope_stack, scope_map);
}
}
ast::ExprAssign(ref sub_exp1, ref sub_exp2) |
ast::ExprRepeat(ref sub_exp1, ref sub_exp2) => {
walk_expr(cx, &**sub_exp1, scope_stack, scope_map);
walk_expr(cx, &**sub_exp2, scope_stack, scope_map);
}
ast::ExprIf(ref cond_exp, ref then_block, ref opt_else_exp) => {
walk_expr(cx, &**cond_exp, scope_stack, scope_map);
with_new_scope(cx,
then_block.span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
walk_block(cx, &**then_block, scope_stack, scope_map);
});
match *opt_else_exp {
Some(ref else_exp) =>
walk_expr(cx, &**else_exp, scope_stack, scope_map),
_ => ()
}
}
ast::ExprIfLet(..) => {
cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \
Found unexpanded if-let.");
}
ast::ExprWhile(ref cond_exp, ref loop_body, _) => {
walk_expr(cx, &**cond_exp, scope_stack, scope_map);
with_new_scope(cx,
loop_body.span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
walk_block(cx, &**loop_body, scope_stack, scope_map);
})
}
ast::ExprWhileLet(..) => {
cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \
Found unexpanded while-let.");
}
ast::ExprForLoop(..) => {
cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \
Found unexpanded for loop.");
}
ast::ExprMac(_) => {
cx.sess().span_bug(exp.span, "debuginfo::create_scope_map() - \
Found unexpanded macro.");
}
ast::ExprLoop(ref block, _) |
ast::ExprBlock(ref block) => {
with_new_scope(cx,
block.span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
walk_block(cx, &**block, scope_stack, scope_map);
})
}
ast::ExprClosure(_, ref decl, ref block) => {
with_new_scope(cx,
block.span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
for &ast::Arg { pat: ref pattern, .. } in &decl.inputs {
walk_pattern(cx, &**pattern, scope_stack, scope_map);
}
walk_block(cx, &**block, scope_stack, scope_map);
})
}
ast::ExprCall(ref fn_exp, ref args) => {
walk_expr(cx, &**fn_exp, scope_stack, scope_map);
for arg_exp in args {
walk_expr(cx, &**arg_exp, scope_stack, scope_map);
}
}
ast::ExprMethodCall(_, _, ref args) => {
for arg_exp in args {
walk_expr(cx, &**arg_exp, scope_stack, scope_map);
}
}
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
// For each arm we have to first walk the pattern as these might
// introduce new artificial scopes. It should be sufficient to
// walk only one pattern per arm, as they all must contain the
// same binding names.
for arm_ref in arms {
let arm_span = arm_ref.pats[0].span;
with_new_scope(cx,
arm_span,
scope_stack,
scope_map,
|cx, scope_stack, scope_map| {
for pat in &arm_ref.pats {
walk_pattern(cx, &**pat, scope_stack, scope_map);
}
if let Some(ref guard_exp) = arm_ref.guard {
walk_expr(cx, &**guard_exp, scope_stack, scope_map)
}
walk_expr(cx, &*arm_ref.body, scope_stack, scope_map);
})
}
}
ast::ExprStruct(_, ref fields, ref base_exp) => {
for &ast::Field { expr: ref exp, .. } in fields {
walk_expr(cx, &**exp, scope_stack, scope_map);
}
match *base_exp {
Some(ref exp) => walk_expr(cx, &**exp, scope_stack, scope_map),
None => ()
}
}
ast::ExprInlineAsm(ast::InlineAsm { ref inputs,
ref outputs,
.. }) => {
// inputs, outputs: Vec<(String, P<Expr>)>
for &(_, ref exp) in inputs {
walk_expr(cx, &**exp, scope_stack, scope_map);
}
for &(_, ref exp, _) in outputs {
walk_expr(cx, &**exp, scope_stack, scope_map);
}
}
}
}

View File

@ -0,0 +1,189 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! # Debug Info Module
//!
//! This module serves the purpose of generating debug symbols. We use LLVM's
//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html)
//! features for generating the debug information. The general principle is
//! this:
//!
//! Given the right metadata in the LLVM IR, the LLVM code generator is able to
//! create DWARF debug symbols for the given code. The
//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured
//! much like DWARF *debugging information entries* (DIE), representing type
//! information such as datatype layout, function signatures, block layout,
//! variable location and scope information, etc. It is the purpose of this
//! module to generate correct metadata and insert it into the LLVM IR.
//!
//! As the exact format of metadata trees may change between different LLVM
//! versions, we now use LLVM
//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html)
//! to create metadata where possible. This will hopefully ease the adaption of
//! this module to future LLVM versions.
//!
//! The public API of the module is a set of functions that will insert the
//! correct metadata into the LLVM IR when called with the right parameters.
//! The module is thus driven from an outside client with functions like
//! `debuginfo::create_local_var_metadata(bcx: block, local: &ast::local)`.
//!
//! Internally the module will try to reuse already created metadata by
//! utilizing a cache. The way to get a shared metadata node when needed is
//! thus to just call the corresponding function in this module:
//!
//! let file_metadata = file_metadata(crate_context, path);
//!
//! The function will take care of probing the cache for an existing node for
//! that exact file path.
//!
//! All private state used by the module is stored within either the
//! CrateDebugContext struct (owned by the CrateContext) or the
//! FunctionDebugContext (owned by the FunctionContext).
//!
//! This file consists of three conceptual sections:
//! 1. The public interface of the module
//! 2. Module-internal metadata creation functions
//! 3. Minor utility functions
//!
//!
//! ## Recursive Types
//!
//! Some kinds of types, such as structs and enums can be recursive. That means
//! that the type definition of some type X refers to some other type which in
//! turn (transitively) refers to X. This introduces cycles into the type
//! referral graph. A naive algorithm doing an on-demand, depth-first traversal
//! of this graph when describing types, can get trapped in an endless loop
//! when it reaches such a cycle.
//!
//! For example, the following simple type for a singly-linked list...
//!
//! ```
//! struct List {
//! value: int,
//! tail: Option<Box<List>>,
//! }
//! ```
//!
//! will generate the following callstack with a naive DFS algorithm:
//!
//! ```
//! describe(t = List)
//! describe(t = int)
//! describe(t = Option<Box<List>>)
//! describe(t = Box<List>)
//! describe(t = List) // at the beginning again...
//! ...
//! ```
//!
//! To break cycles like these, we use "forward declarations". That is, when
//! the algorithm encounters a possibly recursive type (any struct or enum), it
//! immediately creates a type description node and inserts it into the cache
//! *before* describing the members of the type. This type description is just
//! a stub (as type members are not described and added to it yet) but it
//! allows the algorithm to already refer to the type. After the stub is
//! inserted into the cache, the algorithm continues as before. If it now
//! encounters a recursive reference, it will hit the cache and does not try to
//! describe the type anew.
//!
//! This behaviour is encapsulated in the 'RecursiveTypeDescription' enum,
//! which represents a kind of continuation, storing all state needed to
//! continue traversal at the type members after the type has been registered
//! with the cache. (This implementation approach might be a tad over-
//! engineered and may change in the future)
//!
//!
//! ## Source Locations and Line Information
//!
//! In addition to data type descriptions the debugging information must also
//! allow to map machine code locations back to source code locations in order
//! to be useful. This functionality is also handled in this module. The
//! following functions allow to control source mappings:
//!
//! + set_source_location()
//! + clear_source_location()
//! + start_emitting_source_locations()
//!
//! `set_source_location()` allows to set the current source location. All IR
//! instructions created after a call to this function will be linked to the
//! given source location, until another location is specified with
//! `set_source_location()` or the source location is cleared with
//! `clear_source_location()`. In the later case, subsequent IR instruction
//! will not be linked to any source location. As you can see, this is a
//! stateful API (mimicking the one in LLVM), so be careful with source
//! locations set by previous calls. It's probably best to not rely on any
//! specific state being present at a given point in code.
//!
//! One topic that deserves some extra attention is *function prologues*. At
//! the beginning of a function's machine code there are typically a few
//! instructions for loading argument values into allocas and checking if
//! there's enough stack space for the function to execute. This *prologue* is
//! not visible in the source code and LLVM puts a special PROLOGUE END marker
//! into the line table at the first non-prologue instruction of the function.
//! In order to find out where the prologue ends, LLVM looks for the first
//! instruction in the function body that is linked to a source location. So,
//! when generating prologue instructions we have to make sure that we don't
//! emit source location information until the 'real' function body begins. For
//! this reason, source location emission is disabled by default for any new
//! function being translated and is only activated after a call to the third
//! function from the list above, `start_emitting_source_locations()`. This
//! function should be called right before regularly starting to translate the
//! top-level block of the given function.
//!
//! There is one exception to the above rule: `llvm.dbg.declare` instruction
//! must be linked to the source location of the variable being declared. For
//! function parameters these `llvm.dbg.declare` instructions typically occur
//! in the middle of the prologue, however, they are ignored by LLVM's prologue
//! detection. The `create_argument_metadata()` and related functions take care
//! of linking the `llvm.dbg.declare` instructions to the correct source
//! locations even while source location emission is still disabled, so there
//! is no need to do anything special with source location handling here.
//!
//! ## Unique Type Identification
//!
//! In order for link-time optimization to work properly, LLVM needs a unique
//! type identifier that tells it across compilation units which types are the
//! same as others. This type identifier is created by
//! TypeMap::get_unique_type_id_of_type() using the following algorithm:
//!
//! (1) Primitive types have their name as ID
//! (2) Structs, enums and traits have a multipart identifier
//!
//! (1) The first part is the SVH (strict version hash) of the crate they
//! wereoriginally defined in
//!
//! (2) The second part is the ast::NodeId of the definition in their
//! originalcrate
//!
//! (3) The final part is a concatenation of the type IDs of their concrete
//! typearguments if they are generic types.
//!
//! (3) Tuple-, pointer and function types are structurally identified, which
//! means that they are equivalent if their component types are equivalent
//! (i.e. (int, int) is the same regardless in which crate it is used).
//!
//! This algorithm also provides a stable ID for types that are defined in one
//! crate but instantiated from metadata within another crate. We just have to
//! take care to always map crate and node IDs back to the original crate
//! context.
//!
//! As a side-effect these unique type IDs also help to solve a problem arising
//! from lifetime parameters. Since lifetime parameters are completely omitted
//! in debuginfo, more than one `Ty` instance may map to the same debuginfo
//! type metadata, that is, some struct `Struct<'a>` may have N instantiations
//! with different concrete substitutions for `'a`, and thus there will be N
//! `Ty` instances for the type `Struct<'a>` even though it is not generic
//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as
//! cheap identifier for type metadata---we have done this in the past, but it
//! led to unnecessary metadata duplication in the best case and LLVM
//! assertions in the worst. However, the unique type ID as described above
//! *can* be used as identifier. Since it is comparatively expensive to
//! construct, though, `ty::type_id()` is still used additionally as an
//! optimization for cases where the exact same type has been seen before
//! (which is most of the time).

View File

@ -0,0 +1,94 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// .debug_gdb_scripts binary section.
use llvm;
use llvm::ValueRef;
use trans::common::{C_bytes, CrateContext};
use trans::declare;
use trans::type_::Type;
use middle::ty::ClosureTyper;
use session::config::NoDebugInfo;
use std::ffi::CString;
use std::ptr;
use syntax::attr;
/// Inserts a side-effect free instruction sequence that makes sure that the
/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker.
pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) {
if needs_gdb_debug_scripts_section(ccx) {
let empty = CString::new("").unwrap();
let gdb_debug_scripts_section_global =
get_or_insert_gdb_debug_scripts_section_global(ccx);
unsafe {
let volative_load_instruction =
llvm::LLVMBuildLoad(ccx.raw_builder(),
gdb_debug_scripts_section_global,
empty.as_ptr());
llvm::LLVMSetVolatile(volative_load_instruction, llvm::True);
}
}
}
/// Allocates the global variable responsible for the .debug_gdb_scripts binary
/// section.
pub fn get_or_insert_gdb_debug_scripts_section_global(ccx: &CrateContext)
-> llvm::ValueRef {
let section_var_name = "__rustc_debug_gdb_scripts_section__";
let section_var = unsafe {
llvm::LLVMGetNamedGlobal(ccx.llmod(),
section_var_name.as_ptr() as *const _)
};
if section_var == ptr::null_mut() {
let section_name = b".debug_gdb_scripts\0";
let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0";
unsafe {
let llvm_type = Type::array(&Type::i8(ccx),
section_contents.len() as u64);
let section_var = declare::define_global(ccx, section_var_name,
llvm_type).unwrap_or_else(||{
ccx.sess().bug(&format!("symbol `{}` is already defined", section_var_name))
});
llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _);
llvm::LLVMSetInitializer(section_var, C_bytes(ccx, section_contents));
llvm::LLVMSetGlobalConstant(section_var, llvm::True);
llvm::LLVMSetUnnamedAddr(section_var, llvm::True);
llvm::SetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage);
// This should make sure that the whole section is not larger than
// the string it contains. Otherwise we get a warning from GDB.
llvm::LLVMSetAlignment(section_var, 1);
section_var
}
} else {
section_var
}
}
pub fn needs_gdb_debug_scripts_section(ccx: &CrateContext) -> bool {
let omit_gdb_pretty_printer_section =
attr::contains_name(&ccx.tcx()
.map
.krate()
.attrs,
"omit_gdb_pretty_printer_section");
!omit_gdb_pretty_printer_section &&
!ccx.sess().target.target.options.is_like_osx &&
!ccx.sess().target.target.options.is_like_windows &&
ccx.sess().opts.debuginfo != NoDebugInfo
}

View File

@ -0,0 +1,651 @@
// Copyright 2012-2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// See doc.rs for documentation.
mod doc;
use self::VariableAccess::*;
use self::VariableKind::*;
use self::utils::{DIB, span_start, assert_type_for_node_id, contains_nodebug_attribute,
create_DIArray, is_node_local_to_unit};
use self::namespace::{namespace_for_item, NamespaceTreeNode};
use self::type_names::compute_debuginfo_type_name;
use self::metadata::{type_metadata, file_metadata, scope_metadata, TypeMap, compile_unit_metadata};
use self::source_loc::InternalDebugLocation;
use llvm;
use llvm::{ModuleRef, ContextRef, ValueRef};
use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray,
DIDescriptor, FlagPrototyped};
use middle::subst::{self, Substs};
use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext, Block};
use trans;
use trans::monomorphize;
use middle::ty::{self, Ty, ClosureTyper};
use session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
use util::nodemap::{DefIdMap, NodeMap, FnvHashMap, FnvHashSet};
use libc::c_uint;
use std::cell::{Cell, RefCell};
use std::ffi::CString;
use std::ptr;
use std::rc::Rc;
use syntax::codemap::{Span, Pos};
use syntax::{ast, codemap, ast_util, ast_map};
use syntax::parse::token::{self, special_idents};
pub mod gdb;
mod utils;
mod namespace;
mod type_names;
mod metadata;
mod create_scope_map;
mod source_loc;
pub use self::source_loc::set_source_location;
pub use self::source_loc::clear_source_location;
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;
pub use self::metadata::create_match_binding_metadata;
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;
#[allow(non_upper_case_globals)]
const DW_TAG_auto_variable: c_uint = 0x100;
#[allow(non_upper_case_globals)]
const DW_TAG_arg_variable: c_uint = 0x101;
/// A context object for maintaining all state needed by the debuginfo module.
pub struct CrateDebugContext<'tcx> {
llcontext: ContextRef,
builder: DIBuilderRef,
current_debug_location: Cell<InternalDebugLocation>,
created_files: RefCell<FnvHashMap<String, DIFile>>,
created_enum_disr_types: RefCell<DefIdMap<DIType>>,
type_map: RefCell<TypeMap<'tcx>>,
namespace_map: RefCell<FnvHashMap<Vec<ast::Name>, Rc<NamespaceTreeNode>>>,
// This collection is used to assert that composite types (structs, enums,
// ...) have their members only set once:
composite_types_completed: RefCell<FnvHashSet<DIType>>,
}
impl<'tcx> CrateDebugContext<'tcx> {
pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> {
debug!("CrateDebugContext::new");
let builder = unsafe { llvm::LLVMDIBuilderCreate(llmod) };
// DIBuilder inherits context from the module, so we'd better use the same one
let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
return CrateDebugContext {
llcontext: llcontext,
builder: builder,
current_debug_location: Cell::new(InternalDebugLocation::UnknownLocation),
created_files: RefCell::new(FnvHashMap()),
created_enum_disr_types: RefCell::new(DefIdMap()),
type_map: RefCell::new(TypeMap::new()),
namespace_map: RefCell::new(FnvHashMap()),
composite_types_completed: RefCell::new(FnvHashSet()),
};
}
}
pub enum FunctionDebugContext {
RegularContext(Box<FunctionDebugContextData>),
DebugInfoDisabled,
FunctionWithoutDebugInfo,
}
impl FunctionDebugContext {
fn get_ref<'a>(&'a self,
cx: &CrateContext,
span: Span)
-> &'a FunctionDebugContextData {
match *self {
FunctionDebugContext::RegularContext(box ref data) => data,
FunctionDebugContext::DebugInfoDisabled => {
cx.sess().span_bug(span,
FunctionDebugContext::debuginfo_disabled_message());
}
FunctionDebugContext::FunctionWithoutDebugInfo => {
cx.sess().span_bug(span,
FunctionDebugContext::should_be_ignored_message());
}
}
}
fn debuginfo_disabled_message() -> &'static str {
"debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!"
}
fn should_be_ignored_message() -> &'static str {
"debuginfo: Error trying to access FunctionDebugContext for function that should be \
ignored by debug info!"
}
}
struct FunctionDebugContextData {
scope_map: RefCell<NodeMap<DIScope>>,
fn_metadata: DISubprogram,
argument_counter: Cell<usize>,
source_locations_enabled: Cell<bool>,
source_location_override: Cell<bool>,
}
pub enum VariableAccess<'a> {
// The llptr given is an alloca containing the variable's value
DirectVariable { alloca: ValueRef },
// The llptr given is an alloca containing the start of some pointer chain
// leading to the variable's content.
IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] }
}
pub enum VariableKind {
ArgumentVariable(usize /*index*/),
LocalVariable,
CapturedVariable,
}
/// Create any deferred debug metadata nodes
pub fn finalize(cx: &CrateContext) {
if cx.dbg_cx().is_none() {
return;
}
debug!("finalize");
let _ = compile_unit_metadata(cx);
if gdb::needs_gdb_debug_scripts_section(cx) {
// Add a .debug_gdb_scripts section to this compile-unit. This will
// cause GDB to try and load the gdb_load_rust_pretty_printers.py file,
// which activates the Rust pretty printers for binary this section is
// contained in.
gdb::get_or_insert_gdb_debug_scripts_section_global(cx);
}
unsafe {
llvm::LLVMDIBuilderFinalize(DIB(cx));
llvm::LLVMDIBuilderDispose(DIB(cx));
// Debuginfo generation in LLVM by default uses a higher
// version of dwarf than OS X currently understands. We can
// instruct LLVM to emit an older version of dwarf, however,
// for OS X to understand. For more info see #11352
// This can be overridden using --llvm-opts -dwarf-version,N.
// Android has the same issue (#22398)
if cx.sess().target.target.options.is_like_osx ||
cx.sess().target.target.options.is_like_android {
llvm::LLVMRustAddModuleFlag(cx.llmod(),
"Dwarf Version\0".as_ptr() as *const _,
2)
}
// Prevent bitcode readers from deleting the debug info.
let ptr = "Debug Info Version\0".as_ptr();
llvm::LLVMRustAddModuleFlag(cx.llmod(), ptr as *const _,
llvm::LLVMRustDebugMetadataVersion);
};
}
/// Creates the function-specific debug context.
///
/// Returns the FunctionDebugContext for the function which holds state needed
/// for debug info creation. The function may also return another variant of the
/// FunctionDebugContext enum which indicates why no debuginfo should be created
/// for the function.
pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
fn_ast_id: ast::NodeId,
param_substs: &Substs<'tcx>,
llfn: ValueRef) -> FunctionDebugContext {
if cx.sess().opts.debuginfo == NoDebugInfo {
return FunctionDebugContext::DebugInfoDisabled;
}
// Clear the debug location so we don't assign them in the function prelude.
// Do this here already, in case we do an early exit from this function.
source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation);
if fn_ast_id == ast::DUMMY_NODE_ID {
// This is a function not linked to any source location, so don't
// generate debuginfo for it.
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
let empty_generics = ast_util::empty_generics();
let fnitem = cx.tcx().map.get(fn_ast_id);
let (name, fn_decl, generics, top_level_block, span, has_path) = match fnitem {
ast_map::NodeItem(ref item) => {
if contains_nodebug_attribute(&item.attrs) {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
match item.node {
ast::ItemFn(ref fn_decl, _, _, ref generics, ref top_level_block) => {
(item.ident.name, fn_decl, generics, top_level_block, item.span, true)
}
_ => {
cx.sess().span_bug(item.span,
"create_function_debug_context: item bound to non-function");
}
}
}
ast_map::NodeImplItem(impl_item) => {
match impl_item.node {
ast::MethodImplItem(ref sig, ref body) => {
if contains_nodebug_attribute(&impl_item.attrs) {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
(impl_item.ident.name,
&sig.decl,
&sig.generics,
body,
impl_item.span,
true)
}
_ => {
cx.sess().span_bug(impl_item.span,
"create_function_debug_context() \
called on non-method impl item?!")
}
}
}
ast_map::NodeExpr(ref expr) => {
match expr.node {
ast::ExprClosure(_, ref fn_decl, ref top_level_block) => {
let name = format!("fn{}", token::gensym("fn"));
let name = token::intern(&name[..]);
(name, fn_decl,
// This is not quite right. It should actually inherit
// the generics of the enclosing function.
&empty_generics,
top_level_block,
expr.span,
// Don't try to lookup the item path:
false)
}
_ => cx.sess().span_bug(expr.span,
"create_function_debug_context: expected an expr_fn_block here")
}
}
ast_map::NodeTraitItem(trait_item) => {
match trait_item.node {
ast::MethodTraitItem(ref sig, Some(ref body)) => {
if contains_nodebug_attribute(&trait_item.attrs) {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
(trait_item.ident.name,
&sig.decl,
&sig.generics,
body,
trait_item.span,
true)
}
_ => {
cx.sess()
.bug(&format!("create_function_debug_context: \
unexpected sort of node: {:?}",
fnitem))
}
}
}
ast_map::NodeForeignItem(..) |
ast_map::NodeVariant(..) |
ast_map::NodeStructCtor(..) => {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
_ => cx.sess().bug(&format!("create_function_debug_context: \
unexpected sort of node: {:?}",
fnitem))
};
// This can be the case for functions inlined from another crate
if span == codemap::DUMMY_SP {
return FunctionDebugContext::FunctionWithoutDebugInfo;
}
let loc = span_start(cx, span);
let file_metadata = file_metadata(cx, &loc.file.name);
let function_type_metadata = unsafe {
let fn_signature = get_function_signature(cx,
fn_ast_id,
&*fn_decl,
param_substs,
span);
llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature)
};
// Get_template_parameters() will append a `<...>` clause to the function
// name if necessary.
let mut function_name = String::from_str(&token::get_name(name));
let template_parameters = get_template_parameters(cx,
generics,
param_substs,
file_metadata,
&mut function_name);
// There is no ast_map::Path for ast::ExprClosure-type functions. For now,
// just don't put them into a namespace. In the future this could be improved
// somehow (storing a path in the ast_map, or construct a path using the
// enclosing function).
let (linkage_name, containing_scope) = if has_path {
let namespace_node = namespace_for_item(cx, ast_util::local_def(fn_ast_id));
let linkage_name = namespace_node.mangled_name_of_contained_item(
&function_name[..]);
let containing_scope = namespace_node.scope;
(linkage_name, containing_scope)
} else {
(function_name.clone(), file_metadata)
};
// Clang sets this parameter to the opening brace of the function's block,
// so let's do this too.
let scope_line = span_start(cx, top_level_block.span).line;
let is_local_to_unit = is_node_local_to_unit(cx, fn_ast_id);
let function_name = CString::new(function_name).unwrap();
let linkage_name = CString::new(linkage_name).unwrap();
let fn_metadata = unsafe {
llvm::LLVMDIBuilderCreateFunction(
DIB(cx),
containing_scope,
function_name.as_ptr(),
linkage_name.as_ptr(),
file_metadata,
loc.line as c_uint,
function_type_metadata,
is_local_to_unit,
true,
scope_line as c_uint,
FlagPrototyped as c_uint,
cx.sess().opts.optimize != config::No,
llfn,
template_parameters,
ptr::null_mut())
};
let scope_map = create_scope_map::create_scope_map(cx,
&fn_decl.inputs,
&*top_level_block,
fn_metadata,
fn_ast_id);
// Initialize fn debug context (including scope map and namespace map)
let fn_debug_context = box FunctionDebugContextData {
scope_map: RefCell::new(scope_map),
fn_metadata: fn_metadata,
argument_counter: Cell::new(1),
source_locations_enabled: Cell::new(false),
source_location_override: Cell::new(false),
};
return FunctionDebugContext::RegularContext(fn_debug_context);
fn get_function_signature<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
fn_ast_id: ast::NodeId,
fn_decl: &ast::FnDecl,
param_substs: &Substs<'tcx>,
error_reporting_span: Span) -> DIArray {
if cx.sess().opts.debuginfo == LimitedDebugInfo {
return create_DIArray(DIB(cx), &[]);
}
let mut signature = Vec::with_capacity(fn_decl.inputs.len() + 1);
// Return type -- llvm::DIBuilder wants this at index 0
assert_type_for_node_id(cx, fn_ast_id, error_reporting_span);
let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id);
let return_type = monomorphize::apply_param_substs(cx.tcx(),
param_substs,
&return_type);
if ty::type_is_nil(return_type) {
signature.push(ptr::null_mut())
} else {
signature.push(type_metadata(cx, return_type, codemap::DUMMY_SP));
}
// Arguments types
for arg in &fn_decl.inputs {
assert_type_for_node_id(cx, arg.pat.id, arg.pat.span);
let arg_type = ty::node_id_to_type(cx.tcx(), arg.pat.id);
let arg_type = monomorphize::apply_param_substs(cx.tcx(),
param_substs,
&arg_type);
signature.push(type_metadata(cx, arg_type, codemap::DUMMY_SP));
}
return create_DIArray(DIB(cx), &signature[..]);
}
fn get_template_parameters<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
generics: &ast::Generics,
param_substs: &Substs<'tcx>,
file_metadata: DIFile,
name_to_append_suffix_to: &mut String)
-> DIArray
{
let self_type = param_substs.self_ty();
let self_type = monomorphize::normalize_associated_type(cx.tcx(), &self_type);
// Only true for static default methods:
let has_self_type = self_type.is_some();
if !generics.is_type_parameterized() && !has_self_type {
return create_DIArray(DIB(cx), &[]);
}
name_to_append_suffix_to.push('<');
// The list to be filled with template parameters:
let mut template_params: Vec<DIDescriptor> =
Vec::with_capacity(generics.ty_params.len() + 1);
// Handle self type
if has_self_type {
let actual_self_type = self_type.unwrap();
// Add self type name to <...> clause of function name
let actual_self_type_name = compute_debuginfo_type_name(
cx,
actual_self_type,
true);
name_to_append_suffix_to.push_str(&actual_self_type_name[..]);
if generics.is_type_parameterized() {
name_to_append_suffix_to.push_str(",");
}
// Only create type information if full debuginfo is enabled
if cx.sess().opts.debuginfo == FullDebugInfo {
let actual_self_type_metadata = type_metadata(cx,
actual_self_type,
codemap::DUMMY_SP);
let name = token::get_name(special_idents::type_self.name);
let name = CString::new(name.as_bytes()).unwrap();
let param_metadata = unsafe {
llvm::LLVMDIBuilderCreateTemplateTypeParameter(
DIB(cx),
file_metadata,
name.as_ptr(),
actual_self_type_metadata,
ptr::null_mut(),
0,
0)
};
template_params.push(param_metadata);
}
}
// Handle other generic parameters
let actual_types = param_substs.types.get_slice(subst::FnSpace);
for (index, &ast::TyParam{ ident, .. }) in generics.ty_params.iter().enumerate() {
let actual_type = actual_types[index];
// Add actual type name to <...> clause of function name
let actual_type_name = compute_debuginfo_type_name(cx,
actual_type,
true);
name_to_append_suffix_to.push_str(&actual_type_name[..]);
if index != generics.ty_params.len() - 1 {
name_to_append_suffix_to.push_str(",");
}
// Again, only create type information if full debuginfo is enabled
if cx.sess().opts.debuginfo == FullDebugInfo {
let actual_type_metadata = type_metadata(cx, actual_type, codemap::DUMMY_SP);
let ident = token::get_ident(ident);
let name = CString::new(ident.as_bytes()).unwrap();
let param_metadata = unsafe {
llvm::LLVMDIBuilderCreateTemplateTypeParameter(
DIB(cx),
file_metadata,
name.as_ptr(),
actual_type_metadata,
ptr::null_mut(),
0,
0)
};
template_params.push(param_metadata);
}
}
name_to_append_suffix_to.push('>');
return create_DIArray(DIB(cx), &template_params[..]);
}
}
fn declare_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
variable_name: ast::Name,
variable_type: Ty<'tcx>,
scope_metadata: DIScope,
variable_access: VariableAccess,
variable_kind: VariableKind,
span: Span) {
let cx: &CrateContext = bcx.ccx();
let filename = span_start(cx, span).file.name.clone();
let file_metadata = file_metadata(cx, &filename[..]);
let name = token::get_name(variable_name);
let loc = span_start(cx, span);
let type_metadata = type_metadata(cx, variable_type, span);
let (argument_index, dwarf_tag) = match variable_kind {
ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
LocalVariable |
CapturedVariable => (0, DW_TAG_auto_variable)
};
let name = CString::new(name.as_bytes()).unwrap();
match (variable_access, &[][..]) {
(DirectVariable { alloca }, address_operations) |
(IndirectVariable {alloca, address_operations}, _) => {
let metadata = unsafe {
llvm::LLVMDIBuilderCreateVariable(
DIB(cx),
dwarf_tag,
scope_metadata,
name.as_ptr(),
file_metadata,
loc.line as c_uint,
type_metadata,
cx.sess().opts.optimize != config::No,
0,
address_operations.as_ptr(),
address_operations.len() as c_uint,
argument_index)
};
source_loc::set_debug_location(cx, InternalDebugLocation::new(scope_metadata,
loc.line,
loc.col.to_usize()));
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx),
alloca,
metadata,
address_operations.as_ptr(),
address_operations.len() as c_uint,
bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr);
}
}
}
match variable_kind {
ArgumentVariable(_) | CapturedVariable => {
assert!(!bcx.fcx
.debug_context
.get_ref(cx, span)
.source_locations_enabled
.get());
source_loc::set_debug_location(cx, InternalDebugLocation::UnknownLocation);
}
_ => { /* nothing to do */ }
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DebugLoc {
At(ast::NodeId, Span),
None
}
impl DebugLoc {
pub fn apply(&self, fcx: &FunctionContext) {
match *self {
DebugLoc::At(node_id, span) => {
source_loc::set_source_location(fcx, node_id, span);
}
DebugLoc::None => {
source_loc::clear_source_location(fcx);
}
}
}
}
pub trait ToDebugLoc {
fn debug_loc(&self) -> DebugLoc;
}
impl ToDebugLoc for ast::Expr {
fn debug_loc(&self) -> DebugLoc {
DebugLoc::At(self.id, self.span)
}
}
impl ToDebugLoc for NodeIdAndSpan {
fn debug_loc(&self) -> DebugLoc {
DebugLoc::At(self.id, self.span)
}
}
impl ToDebugLoc for Option<NodeIdAndSpan> {
fn debug_loc(&self) -> DebugLoc {
match *self {
Some(NodeIdAndSpan { id, span }) => DebugLoc::At(id, span),
None => DebugLoc::None
}
}
}

View File

@ -0,0 +1,134 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Namespace Handling.
use super::utils::{DIB, debug_context};
use llvm;
use llvm::debuginfo::DIScope;
use trans::common::CrateContext;
use middle::ty::{self, ClosureTyper};
use std::ffi::CString;
use std::ptr;
use std::rc::{Rc, Weak};
use syntax::{ast, ast_map};
use syntax::parse::token;
pub struct NamespaceTreeNode {
pub name: ast::Name,
pub scope: DIScope,
pub parent: Option<Weak<NamespaceTreeNode>>,
}
impl NamespaceTreeNode {
pub fn mangled_name_of_contained_item(&self, item_name: &str) -> String {
fn fill_nested(node: &NamespaceTreeNode, output: &mut String) {
match node.parent {
Some(ref parent) => fill_nested(&*parent.upgrade().unwrap(), output),
None => {}
}
let string = token::get_name(node.name);
output.push_str(&format!("{}", string.len()));
output.push_str(&string);
}
let mut name = String::from_str("_ZN");
fill_nested(self, &mut name);
name.push_str(&format!("{}", item_name.len()));
name.push_str(item_name);
name.push('E');
name
}
}
pub fn crate_root_namespace<'a>(cx: &'a CrateContext) -> &'a str {
&cx.link_meta().crate_name
}
pub fn namespace_for_item(cx: &CrateContext, def_id: ast::DefId) -> Rc<NamespaceTreeNode> {
ty::with_path(cx.tcx(), def_id, |path| {
// prepend crate name if not already present
let krate = if def_id.krate == ast::LOCAL_CRATE {
let crate_namespace_name = token::intern(crate_root_namespace(cx));
Some(ast_map::PathMod(crate_namespace_name))
} else {
None
};
let mut path = krate.into_iter().chain(path).peekable();
let mut current_key = Vec::new();
let mut parent_node: Option<Rc<NamespaceTreeNode>> = None;
// Create/Lookup namespace for each element of the path.
loop {
// Emulate a for loop so we can use peek below.
let path_element = match path.next() {
Some(e) => e,
None => break
};
// Ignore the name of the item (the last path element).
if path.peek().is_none() {
break;
}
let name = path_element.name();
current_key.push(name);
let existing_node = debug_context(cx).namespace_map.borrow()
.get(&current_key).cloned();
let current_node = match existing_node {
Some(existing_node) => existing_node,
None => {
// create and insert
let parent_scope = match parent_node {
Some(ref node) => node.scope,
None => ptr::null_mut()
};
let namespace_name = token::get_name(name);
let namespace_name = CString::new(namespace_name.as_bytes()).unwrap();
let scope = unsafe {
llvm::LLVMDIBuilderCreateNameSpace(
DIB(cx),
parent_scope,
namespace_name.as_ptr(),
// cannot reconstruct file ...
ptr::null_mut(),
// ... or line information, but that's not so important.
0)
};
let node = Rc::new(NamespaceTreeNode {
name: name,
scope: scope,
parent: parent_node.map(|parent| parent.downgrade()),
});
debug_context(cx).namespace_map.borrow_mut()
.insert(current_key.clone(), node.clone());
node
}
};
parent_node = Some(current_node);
}
match parent_node {
Some(node) => node,
None => {
cx.sess().bug(&format!("debuginfo::namespace_for_item(): \
path too short for {:?}",
def_id));
}
}
})
}

View File

@ -0,0 +1,231 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use self::InternalDebugLocation::*;
use super::utils::{debug_context, span_start, fn_should_be_ignored};
use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER};
use super::{FunctionDebugContext, DebugLoc};
use llvm;
use llvm::debuginfo::DIScope;
use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext};
use libc::c_uint;
use std::ptr;
use syntax::codemap::{Span, Pos};
use syntax::{ast, codemap};
pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
node_id: ast::NodeId,
node_span: Span,
is_block: bool)
-> NodeIdAndSpan {
// A debug location needs two things:
// (1) A span (of which only the beginning will actually be used)
// (2) An AST node-id which will be used to look up the lexical scope
// for the location in the functions scope-map
//
// This function will calculate the debug location for compiler-generated
// cleanup calls that are executed when control-flow leaves the
// scope identified by `node_id`.
//
// For everything but block-like things we can simply take id and span of
// the given expression, meaning that from a debugger's view cleanup code is
// executed at the same source location as the statement/expr itself.
//
// Blocks are a special case. Here we want the cleanup to be linked to the
// closing curly brace of the block. The *scope* the cleanup is executed in
// is up to debate: It could either still be *within* the block being
// cleaned up, meaning that locals from the block are still visible in the
// debugger.
// Or it could be in the scope that the block is contained in, so any locals
// from within the block are already considered out-of-scope and thus not
// accessible in the debugger anymore.
//
// The current implementation opts for the second option: cleanup of a block
// already happens in the parent scope of the block. The main reason for
// this decision is that scoping becomes controlflow dependent when variable
// shadowing is involved and it's impossible to decide statically which
// scope is actually left when the cleanup code is executed.
// In practice it shouldn't make much of a difference.
let mut cleanup_span = node_span;
if is_block {
// Not all blocks actually have curly braces (e.g. simple closure
// bodies), in which case we also just want to return the span of the
// whole expression.
let code_snippet = cx.sess().codemap().span_to_snippet(node_span);
if let Ok(code_snippet) = code_snippet {
let bytes = code_snippet.as_bytes();
if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" {
cleanup_span = Span {
lo: node_span.hi - codemap::BytePos(1),
hi: node_span.hi,
expn_id: node_span.expn_id
};
}
}
}
NodeIdAndSpan {
id: node_id,
span: cleanup_span
}
}
/// Sets the current debug location at the beginning of the span.
///
/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id
/// parameter is used to reliably find the correct visibility scope for the code
/// position.
pub fn set_source_location(fcx: &FunctionContext,
node_id: ast::NodeId,
span: Span) {
match fcx.debug_context {
FunctionDebugContext::DebugInfoDisabled => return,
FunctionDebugContext::FunctionWithoutDebugInfo => {
set_debug_location(fcx.ccx, UnknownLocation);
return;
}
FunctionDebugContext::RegularContext(box ref function_debug_context) => {
if function_debug_context.source_location_override.get() {
// Just ignore any attempts to set a new debug location while
// the override is active.
return;
}
let cx = fcx.ccx;
debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span));
if function_debug_context.source_locations_enabled.get() {
let loc = span_start(cx, span);
let scope = scope_metadata(fcx, node_id, span);
set_debug_location(cx, InternalDebugLocation::new(scope,
loc.line,
loc.col.to_usize()));
} else {
set_debug_location(cx, UnknownLocation);
}
}
}
}
/// This function makes sure that all debug locations emitted while executing
/// `wrapped_function` are set to the given `debug_loc`.
pub fn with_source_location_override<F, R>(fcx: &FunctionContext,
debug_loc: DebugLoc,
wrapped_function: F) -> R
where F: FnOnce() -> R
{
match fcx.debug_context {
FunctionDebugContext::DebugInfoDisabled => {
wrapped_function()
}
FunctionDebugContext::FunctionWithoutDebugInfo => {
set_debug_location(fcx.ccx, UnknownLocation);
wrapped_function()
}
FunctionDebugContext::RegularContext(box ref function_debug_context) => {
if function_debug_context.source_location_override.get() {
wrapped_function()
} else {
debug_loc.apply(fcx);
function_debug_context.source_location_override.set(true);
let result = wrapped_function();
function_debug_context.source_location_override.set(false);
result
}
}
}
}
/// Clears the current debug location.
///
/// Instructions generated hereafter won't be assigned a source location.
pub fn clear_source_location(fcx: &FunctionContext) {
if fn_should_be_ignored(fcx) {
return;
}
set_debug_location(fcx.ccx, UnknownLocation);
}
/// Enables emitting source locations for the given functions.
///
/// Since we don't want source locations to be emitted for the function prelude,
/// they are disabled when beginning to translate a new function. This functions
/// switches source location emitting on and must therefore be called before the
/// first real statement/expression of the function is translated.
pub fn start_emitting_source_locations(fcx: &FunctionContext) {
match fcx.debug_context {
FunctionDebugContext::RegularContext(box ref data) => {
data.source_locations_enabled.set(true)
},
_ => { /* safe to ignore */ }
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum InternalDebugLocation {
KnownLocation { scope: DIScope, line: usize, col: usize },
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, debug_location: InternalDebugLocation) {
if debug_location == debug_context(cx).current_debug_location.get() {
return;
}
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);
unsafe {
metadata_node = llvm::LLVMDIBuilderCreateDebugLocation(
debug_context(cx).llcontext,
line as c_uint,
col as c_uint,
scope,
ptr::null_mut());
}
}
UnknownLocation => {
debug!("clearing debug location ");
metadata_node = ptr::null_mut();
}
};
unsafe {
llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node);
}
debug_context(cx).current_debug_location.set(debug_location);
}

View File

@ -0,0 +1,230 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Type Names for Debug Info.
use super::namespace::crate_root_namespace;
use trans::common::CrateContext;
use middle::subst::{self, Substs};
use middle::ty::{self, Ty, ClosureTyper};
use syntax::ast;
use syntax::parse::token;
use util::ppaux;
// Compute the name of the type as it should be stored in debuginfo. Does not do
// any caching, i.e. calling the function twice with the same type will also do
// the work twice. The `qualified` parameter only affects the first level of the
// type name, further levels (i.e. type parameters) are always fully qualified.
pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
qualified: bool)
-> String {
let mut result = String::with_capacity(64);
push_debuginfo_type_name(cx, t, qualified, &mut result);
result
}
// Pushes the name of the type as it should be stored in debuginfo on the
// `output` String. See also compute_debuginfo_type_name().
pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
t: Ty<'tcx>,
qualified: bool,
output: &mut String) {
match t.sty {
ty::ty_bool => output.push_str("bool"),
ty::ty_char => output.push_str("char"),
ty::ty_str => output.push_str("str"),
ty::ty_int(ast::TyIs) => output.push_str("isize"),
ty::ty_int(ast::TyI8) => output.push_str("i8"),
ty::ty_int(ast::TyI16) => output.push_str("i16"),
ty::ty_int(ast::TyI32) => output.push_str("i32"),
ty::ty_int(ast::TyI64) => output.push_str("i64"),
ty::ty_uint(ast::TyUs) => output.push_str("usize"),
ty::ty_uint(ast::TyU8) => output.push_str("u8"),
ty::ty_uint(ast::TyU16) => output.push_str("u16"),
ty::ty_uint(ast::TyU32) => output.push_str("u32"),
ty::ty_uint(ast::TyU64) => output.push_str("u64"),
ty::ty_float(ast::TyF32) => output.push_str("f32"),
ty::ty_float(ast::TyF64) => output.push_str("f64"),
ty::ty_struct(def_id, substs) |
ty::ty_enum(def_id, substs) => {
push_item_name(cx, def_id, qualified, output);
push_type_params(cx, substs, output);
},
ty::ty_tup(ref component_types) => {
output.push('(');
for &component_type in component_types {
push_debuginfo_type_name(cx, component_type, true, output);
output.push_str(", ");
}
if !component_types.is_empty() {
output.pop();
output.pop();
}
output.push(')');
},
ty::ty_uniq(inner_type) => {
output.push_str("Box<");
push_debuginfo_type_name(cx, inner_type, true, output);
output.push('>');
},
ty::ty_ptr(ty::mt { ty: inner_type, mutbl } ) => {
output.push('*');
match mutbl {
ast::MutImmutable => output.push_str("const "),
ast::MutMutable => output.push_str("mut "),
}
push_debuginfo_type_name(cx, inner_type, true, output);
},
ty::ty_rptr(_, ty::mt { ty: inner_type, mutbl }) => {
output.push('&');
if mutbl == ast::MutMutable {
output.push_str("mut ");
}
push_debuginfo_type_name(cx, inner_type, true, output);
},
ty::ty_vec(inner_type, optional_length) => {
output.push('[');
push_debuginfo_type_name(cx, inner_type, true, output);
match optional_length {
Some(len) => {
output.push_str(&format!("; {}", len));
}
None => { /* nothing to do */ }
};
output.push(']');
},
ty::ty_trait(ref trait_data) => {
let principal = ty::erase_late_bound_regions(cx.tcx(), &trait_data.principal);
push_item_name(cx, principal.def_id, false, output);
push_type_params(cx, principal.substs, output);
},
ty::ty_bare_fn(_, &ty::BareFnTy{ unsafety, abi, ref sig } ) => {
if unsafety == ast::Unsafety::Unsafe {
output.push_str("unsafe ");
}
if abi != ::syntax::abi::Rust {
output.push_str("extern \"");
output.push_str(abi.name());
output.push_str("\" ");
}
output.push_str("fn(");
let sig = ty::erase_late_bound_regions(cx.tcx(), sig);
if !sig.inputs.is_empty() {
for &parameter_type in &sig.inputs {
push_debuginfo_type_name(cx, parameter_type, true, output);
output.push_str(", ");
}
output.pop();
output.pop();
}
if sig.variadic {
if !sig.inputs.is_empty() {
output.push_str(", ...");
} else {
output.push_str("...");
}
}
output.push(')');
match sig.output {
ty::FnConverging(result_type) if ty::type_is_nil(result_type) => {}
ty::FnConverging(result_type) => {
output.push_str(" -> ");
push_debuginfo_type_name(cx, result_type, true, output);
}
ty::FnDiverging => {
output.push_str(" -> !");
}
}
},
ty::ty_closure(..) => {
output.push_str("closure");
}
ty::ty_err |
ty::ty_infer(_) |
ty::ty_projection(..) |
ty::ty_param(_) => {
cx.sess().bug(&format!("debuginfo: Trying to create type name for \
unexpected type: {}", ppaux::ty_to_string(cx.tcx(), t)));
}
}
fn push_item_name(cx: &CrateContext,
def_id: ast::DefId,
qualified: bool,
output: &mut String) {
ty::with_path(cx.tcx(), def_id, |path| {
if qualified {
if def_id.krate == ast::LOCAL_CRATE {
output.push_str(crate_root_namespace(cx));
output.push_str("::");
}
let mut path_element_count = 0;
for path_element in path {
let name = token::get_name(path_element.name());
output.push_str(&name);
output.push_str("::");
path_element_count += 1;
}
if path_element_count == 0 {
cx.sess().bug("debuginfo: Encountered empty item path!");
}
output.pop();
output.pop();
} else {
let name = token::get_name(path.last()
.expect("debuginfo: Empty item path?")
.name());
output.push_str(&name);
}
});
}
// Pushes the type parameters in the given `Substs` to the output string.
// This ignores region parameters, since they can't reliably be
// reconstructed for items from non-local crates. For local crates, this
// would be possible but with inlining and LTO we have to use the least
// common denominator - otherwise we would run into conflicts.
fn push_type_params<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
substs: &subst::Substs<'tcx>,
output: &mut String) {
if substs.types.is_empty() {
return;
}
output.push('<');
for &type_parameter in substs.types.iter() {
push_debuginfo_type_name(cx, type_parameter, true, output);
output.push_str(", ");
}
output.pop();
output.pop();
output.push('>');
}
}

View File

@ -0,0 +1,108 @@
// Copyright 2015 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Utility Functions.
use super::{FunctionDebugContext, CrateDebugContext};
use super::namespace::namespace_for_item;
use llvm;
use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray};
use trans::machine;
use trans::common::{CrateContext, FunctionContext};
use trans::type_::Type;
use syntax::codemap::Span;
use syntax::{ast, codemap};
pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool
{
// The is_local_to_unit flag indicates whether a function is local to the
// current compilation unit (i.e. if it is *static* in the C-sense). The
// *reachable* set should provide a good approximation of this, as it
// contains everything that might leak out of the current crate (by being
// externally visible or by being inlined into something externally
// visible). It might better to use the `exported_items` set from
// `driver::CrateAnalysis` in the future, but (atm) this set is not
// available in the translation pass.
!cx.reachable().contains(&node_id)
}
#[allow(non_snake_case)]
pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray {
return unsafe {
llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32)
};
}
pub fn contains_nodebug_attribute(attributes: &[ast::Attribute]) -> bool {
attributes.iter().any(|attr| {
let meta_item: &ast::MetaItem = &*attr.node.value;
match meta_item.node {
ast::MetaWord(ref value) => &value[..] == "no_debug",
_ => false
}
})
}
/// Return codemap::Loc corresponding to the beginning of the span
pub fn span_start(cx: &CrateContext, span: Span) -> codemap::Loc {
cx.sess().codemap().lookup_char_pos(span.lo)
}
pub fn size_and_align_of(cx: &CrateContext, llvm_type: Type) -> (u64, u64) {
(machine::llsize_of_alloc(cx, llvm_type), machine::llalign_of_min(cx, llvm_type) as u64)
}
pub fn bytes_to_bits(bytes: u64) -> u64 {
bytes * 8
}
#[inline]
pub fn debug_context<'a, 'tcx>(cx: &'a CrateContext<'a, 'tcx>)
-> &'a CrateDebugContext<'tcx> {
let debug_context: &'a CrateDebugContext<'tcx> = cx.dbg_cx().as_ref().unwrap();
debug_context
}
#[inline]
#[allow(non_snake_case)]
pub fn DIB(cx: &CrateContext) -> DIBuilderRef {
cx.dbg_cx().as_ref().unwrap().builder
}
pub fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
match fcx.debug_context {
FunctionDebugContext::RegularContext(_) => false,
_ => true
}
}
pub fn assert_type_for_node_id(cx: &CrateContext,
node_id: ast::NodeId,
error_reporting_span: Span) {
if !cx.tcx().node_types().contains_key(&node_id) {
cx.sess().span_bug(error_reporting_span,
"debuginfo: Could not find type for node id!");
}
}
pub fn get_namespace_and_span_for_item(cx: &CrateContext, def_id: ast::DefId)
-> (DIScope, Span) {
let containing_scope = namespace_for_item(cx, def_id).scope;
let definition_span = if def_id.krate == ast::LOCAL_CRATE {
cx.tcx().map.span(def_id.node)
} else {
// For external items there is no span information
codemap::DUMMY_SP
};
(containing_scope, definition_span)
}