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:
commit
3434469b51
@ -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) {
|
||||
|
514
src/librustc_trans/trans/debuginfo/create_scope_map.rs
Normal file
514
src/librustc_trans/trans/debuginfo/create_scope_map.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
189
src/librustc_trans/trans/debuginfo/doc.rs
Normal file
189
src/librustc_trans/trans/debuginfo/doc.rs
Normal 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).
|
94
src/librustc_trans/trans/debuginfo/gdb.rs
Normal file
94
src/librustc_trans/trans/debuginfo/gdb.rs
Normal 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
|
||||
}
|
File diff suppressed because it is too large
Load Diff
651
src/librustc_trans/trans/debuginfo/mod.rs
Normal file
651
src/librustc_trans/trans/debuginfo/mod.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
134
src/librustc_trans/trans/debuginfo/namespace.rs
Normal file
134
src/librustc_trans/trans/debuginfo/namespace.rs
Normal 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(¤t_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));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
231
src/librustc_trans/trans/debuginfo/source_loc.rs
Normal file
231
src/librustc_trans/trans/debuginfo/source_loc.rs
Normal 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);
|
||||
}
|
230
src/librustc_trans/trans/debuginfo/type_names.rs
Normal file
230
src/librustc_trans/trans/debuginfo/type_names.rs
Normal 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 ¶meter_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('>');
|
||||
}
|
||||
}
|
||||
|
108
src/librustc_trans/trans/debuginfo/utils.rs
Normal file
108
src/librustc_trans/trans/debuginfo/utils.rs
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user