auto merge of #7255 : michaelwoerister/rust/debuginfo, r=jdm

This PR contains no real code changes. Just some documentation additions in the form of comments and some internal reordering of functions within debuginfo.rs.
This commit is contained in:
bors 2013-06-26 05:20:06 -07:00
commit a30ab764e1

View File

@ -8,6 +8,39 @@
// 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(bcx: block, local: @ast::local)`.
Internally the module will try to reuse already created metadata by utilizing a cache. All private
state used by the module is stored within a DebugContext struct, which in turn is contained in the
CrateContext.
This file consists of three conceptual sections:
1. The public interface of the module
2. Module-internal metadata creation functions
3. Minor utility functions
*/
use core::prelude::*;
use driver::session;
@ -34,20 +67,8 @@ use syntax::{ast, codemap, ast_util, ast_map};
static DW_LANG_RUST: int = 0x9000;
static CompileUnitTag: int = 17;
static FileDescriptorTag: int = 41;
static SubprogramTag: int = 46;
static SubroutineTag: int = 21;
static BasicTypeDescriptorTag: int = 36;
static AutoVariableTag: int = 256;
static ArgVariableTag: int = 257;
static ReturnVariableTag: int = 258;
static LexicalBlockTag: int = 11;
static PointerTypeTag: int = 15;
static StructureTypeTag: int = 19;
static MemberTag: int = 13;
static ArrayTypeTag: int = 1;
static SubrangeTag: int = 33;
static DW_ATE_boolean: int = 0x02;
static DW_ATE_float: int = 0x04;
@ -56,8 +77,14 @@ static DW_ATE_signed_char: int = 0x06;
static DW_ATE_unsigned: int = 0x07;
static DW_ATE_unsigned_char: int = 0x08;
////////////////
//=-------------------------------------------------------------------------------------------------
// Public Interface of debuginfo module
//=-------------------------------------------------------------------------------------------------
/// A context object for maintaining all state needed by the debuginfo module.
pub struct DebugContext {
names: namegen,
crate_file: ~str,
@ -90,16 +117,6 @@ impl DebugContext {
}
}
#[inline]
fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext {
cx.dbg_cx.get_mut_ref()
}
#[inline]
fn DIB(cx: &CrateContext) -> DIBuilderRef {
cx.dbg_cx.get_ref().builder
}
/// Create any deferred debug metadata nodes
pub fn finalize(cx: @mut CrateContext) {
debug!("finalize");
@ -110,6 +127,207 @@ pub fn finalize(cx: @mut CrateContext) {
};
}
/// Creates debug information for the given local variable.
///
/// Adds the created metadata nodes directly to the crate's IR.
/// The return value should be ignored if called from outside of the debuginfo module.
pub fn create_local_var(bcx: block, local: @ast::local) -> DIVariable {
let cx = bcx.ccx();
let ident = match local.node.pat.node {
ast::pat_ident(_, pth, _) => ast_util::path_to_ident(pth),
// FIXME this should be handled (#2533)
_ => {
bcx.sess().span_note(local.span, "debuginfo for pattern bindings NYI");
return ptr::null();
}
};
let name: &str = cx.sess.str_of(ident);
debug!("create_local_var: %s", name);
let loc = span_start(cx, local.span);
let ty = node_id_type(bcx, local.node.id);
let tymd = create_ty(cx, ty, local.node.ty.span);
let filemd = create_file(cx, loc.file.name);
let context = match bcx.parent {
None => create_function(bcx.fcx),
Some(_) => create_block(bcx)
};
let var_md = do as_c_str(name) |name| { unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx), AutoVariableTag as u32,
context, name, filemd,
loc.line as c_uint, tymd, false, 0, 0)
}};
// FIXME(#6814) Should use `pat_util::pat_bindings` for pats like (a, b) etc
let llptr = match bcx.fcx.lllocals.find_copy(&local.node.pat.id) {
Some(v) => v,
None => {
bcx.tcx().sess.span_bug(
local.span,
fmt!("No entry in lllocals table for %?", local.node.id));
}
};
set_debug_location(cx, create_block(bcx), loc.line, loc.col.to_uint());
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(DIB(cx), llptr, var_md, bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx), instr);
}
return var_md;
}
/// Creates debug information for the given function argument.
///
/// Adds the created metadata nodes directly to the crate's IR.
/// The return value should be ignored if called from outside of the debuginfo module.
pub fn create_arg(bcx: block, arg: ast::arg, span: span) -> Option<DIVariable> {
debug!("create_arg");
if true {
// XXX create_arg disabled for now because "node_id_type(bcx, arg.id)" below blows
// up: "error: internal compiler error: node_id_to_type: no type for node `arg (id=10)`"
return None;
}
let fcx = bcx.fcx;
let cx = fcx.ccx;
let loc = span_start(cx, span);
if "<intrinsic>" == loc.file.name {
return None;
}
let ty = node_id_type(bcx, arg.id);
let tymd = create_ty(cx, ty, arg.ty.span);
let filemd = create_file(cx, loc.file.name);
let context = create_function(fcx);
match arg.pat.node {
ast::pat_ident(_, path, _) => {
// XXX: This is wrong; it should work for multiple bindings.
let ident = path.idents.last();
let name: &str = cx.sess.str_of(*ident);
let mdnode = do as_c_str(name) |name| { unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(DIB(cx),
ArgVariableTag as u32, context, name,
filemd, loc.line as c_uint, tymd, false, 0, 0)
// XXX need to pass in a real argument number
}};
let llptr = fcx.llargs.get_copy(&arg.id);
set_debug_location(cx, create_block(bcx), loc.line, loc.col.to_uint());
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx), llptr, mdnode, bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx), instr);
}
return Some(mdnode);
}
_ => {
return None;
}
}
}
/// Sets the current debug location at the beginning of the span
///
/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...)
pub fn update_source_pos(bcx: block, span: span) {
if !bcx.sess().opts.debuginfo || (*span.lo == 0 && *span.hi == 0) {
return;
}
debug!("update_source_pos: %s", bcx.sess().codemap.span_to_str(span));
let loc = span_start(bcx.ccx(), span);
set_debug_location(bcx.ccx(), create_block(bcx), loc.line, loc.col.to_uint())
}
/// Creates debug information for the given function.
///
/// Adds the created metadata nodes directly to the crate's IR.
/// The return value should be ignored if called from outside of the debuginfo module.
pub fn create_function(fcx: fn_ctxt) -> DISubprogram {
let cx = fcx.ccx;
let fcx = &mut *fcx;
let span = fcx.span.get();
let (ident, ret_ty, id) = match cx.tcx.items.get_copy(&fcx.id) {
ast_map::node_item(item, _) => {
match item.node {
ast::item_fn(ref decl, _, _, _, _) => {
(item.ident, decl.output, item.id)
}
_ => fcx.ccx.sess.span_bug(item.span, "create_function: item bound to non-function")
}
}
ast_map::node_method(method, _, _) => {
(method.ident, method.decl.output, method.id)
}
ast_map::node_expr(expr) => {
match expr.node {
ast::expr_fn_block(ref decl, _) => {
((dbg_cx(cx).names)("fn"), decl.output, expr.id)
}
_ => fcx.ccx.sess.span_bug(expr.span,
"create_function: expected an expr_fn_block here")
}
}
_ => fcx.ccx.sess.bug("create_function: unexpected sort of node")
};
match dbg_cx(cx).created_functions.find(&id) {
Some(fn_md) => return *fn_md,
None => ()
}
debug!("create_function: %s, %s", cx.sess.str_of(ident), cx.sess.codemap.span_to_str(span));
let loc = span_start(cx, span);
let file_md = create_file(cx, loc.file.name);
let ret_ty_md = if cx.sess.opts.extra_debuginfo {
match ret_ty.node {
ast::ty_nil => ptr::null(),
_ => create_ty(cx, ty::node_id_to_type(cx.tcx, id),
ret_ty.span)
}
} else {
ptr::null()
};
let fn_ty = unsafe {
llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx),
file_md, create_DIArray(DIB(cx), [ret_ty_md]))
};
let fn_md =
do as_c_str(cx.sess.str_of(ident)) |name| {
do as_c_str(cx.sess.str_of(ident)) |linkage| { unsafe {
llvm::LLVMDIBuilderCreateFunction(
DIB(cx),
file_md,
name, linkage,
file_md, loc.line as c_uint,
fn_ty, false, true,
loc.line as c_uint,
FlagPrototyped as c_uint,
cx.sess.opts.optimize != session::No,
fcx.llfn, ptr::null(), ptr::null())
}}};
dbg_cx(cx).created_functions.insert(id, fn_md);
return fn_md;
}
//=-------------------------------------------------------------------------------------------------
// Module-Internal debug info creation functions
//=-------------------------------------------------------------------------------------------------
fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray {
return unsafe {
llvm::LLVMDIBuilderGetOrCreateArray(builder, vec::raw::to_ptr(arr), arr.len() as u32)
@ -160,10 +378,7 @@ fn create_file(cx: @mut CrateContext, full_path: &str) -> DIFile {
return file_md;
}
/// Return codemap::Loc corresponding to the beginning of the span
fn span_start(cx: &CrateContext, span: span) -> codemap::Loc {
return cx.sess.codemap.lookup_char_pos(span.lo);
}
fn create_block(bcx: block) -> DILexicalBlock {
let mut bcx = bcx;
@ -205,10 +420,7 @@ fn create_block(bcx: block) -> DILexicalBlock {
return block_md;
}
fn size_and_align_of(cx: @mut CrateContext, t: ty::t) -> (uint, uint) {
let llty = type_of::type_of(cx, t);
(machine::llsize_of_real(cx, llty), machine::llalign_of_min(cx, llty))
}
fn create_basic_type(cx: @mut CrateContext, t: ty::t, _span: span) -> DIType {
let ty_id = ty::type_id(t);
@ -252,6 +464,9 @@ fn create_basic_type(cx: @mut CrateContext, t: ty::t, _span: span) -> DIType {
size * 8 as u64, align * 8 as u64, encoding as c_uint)
}};
// One could think that this call is not necessary, as the create_ty() function will insert the
// type descriptor into the cache anyway. Mind, however, that create_basic_type() is also called
// directly from other functions (e.g. create_boxed_type()).
dbg_cx(cx).created_types.insert(ty_id, ty_md);
return ty_md;
}
@ -324,11 +539,6 @@ impl StructContext {
}
}
#[inline]
fn roundup(x: uint, a: uint) -> uint {
((x + (a - 1)) / a) * a
}
fn create_struct(cx: @mut CrateContext, t: ty::t, fields: ~[ty::field], span: span)
-> DICompositeType {
let loc = span_start(cx, span);
@ -579,103 +789,6 @@ fn create_ty(cx: @mut CrateContext, t: ty::t, span: span) -> DIType {
return ty_md;
}
pub fn create_local_var(bcx: block, local: @ast::local) -> DIVariable {
let cx = bcx.ccx();
let ident = match local.node.pat.node {
ast::pat_ident(_, pth, _) => ast_util::path_to_ident(pth),
// FIXME this should be handled (#2533)
_ => {
bcx.sess().span_note(local.span, "debuginfo for pattern bindings NYI");
return ptr::null();
}
};
let name: &str = cx.sess.str_of(ident);
debug!("create_local_var: %s", name);
let loc = span_start(cx, local.span);
let ty = node_id_type(bcx, local.node.id);
let tymd = create_ty(cx, ty, local.node.ty.span);
let filemd = create_file(cx, loc.file.name);
let context = match bcx.parent {
None => create_function(bcx.fcx),
Some(_) => create_block(bcx)
};
let var_md = do as_c_str(name) |name| { unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(
DIB(cx), AutoVariableTag as u32,
context, name, filemd,
loc.line as c_uint, tymd, false, 0, 0)
}};
// FIXME(#6814) Should use `pat_util::pat_bindings` for pats like (a, b) etc
let llptr = match bcx.fcx.lllocals.find_copy(&local.node.pat.id) {
Some(v) => v,
None => {
bcx.tcx().sess.span_bug(
local.span,
fmt!("No entry in lllocals table for %?", local.node.id));
}
};
set_debug_location(cx, create_block(bcx), loc.line, loc.col.to_uint());
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(DIB(cx), llptr, var_md, bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx), instr);
}
return var_md;
}
pub fn create_arg(bcx: block, arg: ast::arg, span: span) -> Option<DIVariable> {
debug!("create_arg");
if true {
// XXX create_arg disabled for now because "node_id_type(bcx, arg.id)" below blows
// up: "error: internal compiler error: node_id_to_type: no type for node `arg (id=10)`"
return None;
}
let fcx = bcx.fcx;
let cx = fcx.ccx;
let loc = span_start(cx, span);
if "<intrinsic>" == loc.file.name {
return None;
}
let ty = node_id_type(bcx, arg.id);
let tymd = create_ty(cx, ty, arg.ty.span);
let filemd = create_file(cx, loc.file.name);
let context = create_function(fcx);
match arg.pat.node {
ast::pat_ident(_, path, _) => {
// XXX: This is wrong; it should work for multiple bindings.
let ident = path.idents.last();
let name: &str = cx.sess.str_of(*ident);
let mdnode = do as_c_str(name) |name| { unsafe {
llvm::LLVMDIBuilderCreateLocalVariable(DIB(cx),
ArgVariableTag as u32, context, name,
filemd, loc.line as c_uint, tymd, false, 0, 0)
// XXX need to pass in a real argument number
}};
let llptr = fcx.llargs.get_copy(&arg.id);
set_debug_location(cx, create_block(bcx), loc.line, loc.col.to_uint());
unsafe {
let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd(
DIB(cx), llptr, mdnode, bcx.llbb);
llvm::LLVMSetInstDebugLocation(trans::build::B(bcx), instr);
}
return Some(mdnode);
}
_ => {
return None;
}
}
}
fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: uint) {
if dbg_cx(cx).curr_loc == (line, col) {
return;
@ -692,85 +805,34 @@ fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: ui
}
}
/// Set current debug location at the beginning of the span
pub fn update_source_pos(bcx: block, span: span) {
if !bcx.sess().opts.debuginfo || (*span.lo == 0 && *span.hi == 0) {
return;
}
debug!("update_source_pos: %s", bcx.sess().codemap.span_to_str(span));
let loc = span_start(bcx.ccx(), span);
set_debug_location(bcx.ccx(), create_block(bcx), loc.line, loc.col.to_uint())
//=-------------------------------------------------------------------------------------------------
// Utility Functions
//=-------------------------------------------------------------------------------------------------
#[inline]
fn roundup(x: uint, a: uint) -> uint {
((x + (a - 1)) / a) * a
}
pub fn create_function(fcx: fn_ctxt) -> DISubprogram {
let cx = fcx.ccx;
let fcx = &mut *fcx;
let span = fcx.span.get();
let (ident, ret_ty, id) = match cx.tcx.items.get_copy(&fcx.id) {
ast_map::node_item(item, _) => {
match item.node {
ast::item_fn(ref decl, _, _, _, _) => {
(item.ident, decl.output, item.id)
}
_ => fcx.ccx.sess.span_bug(item.span, "create_function: item bound to non-function")
}
}
ast_map::node_method(method, _, _) => {
(method.ident, method.decl.output, method.id)
}
ast_map::node_expr(expr) => {
match expr.node {
ast::expr_fn_block(ref decl, _) => {
((dbg_cx(cx).names)("fn"), decl.output, expr.id)
}
_ => fcx.ccx.sess.span_bug(expr.span,
"create_function: expected an expr_fn_block here")
}
}
_ => fcx.ccx.sess.bug("create_function: unexpected sort of node")
};
match dbg_cx(cx).created_functions.find(&id) {
Some(fn_md) => return *fn_md,
None => ()
}
debug!("create_function: %s, %s", cx.sess.str_of(ident), cx.sess.codemap.span_to_str(span));
let loc = span_start(cx, span);
let file_md = create_file(cx, loc.file.name);
let ret_ty_md = if cx.sess.opts.extra_debuginfo {
match ret_ty.node {
ast::ty_nil => ptr::null(),
_ => create_ty(cx, ty::node_id_to_type(cx.tcx, id),
ret_ty.span)
}
} else {
ptr::null()
};
let fn_ty = unsafe {
llvm::LLVMDIBuilderCreateSubroutineType(DIB(cx),
file_md, create_DIArray(DIB(cx), [ret_ty_md]))
};
let fn_md =
do as_c_str(cx.sess.str_of(ident)) |name| {
do as_c_str(cx.sess.str_of(ident)) |linkage| { unsafe {
llvm::LLVMDIBuilderCreateFunction(
DIB(cx),
file_md,
name, linkage,
file_md, loc.line as c_uint,
fn_ty, false, true,
loc.line as c_uint,
FlagPrototyped as c_uint,
cx.sess.opts.optimize != session::No,
fcx.llfn, ptr::null(), ptr::null())
}}};
dbg_cx(cx).created_functions.insert(id, fn_md);
return fn_md;
/// Return codemap::Loc corresponding to the beginning of the span
fn span_start(cx: &CrateContext, span: span) -> codemap::Loc {
return cx.sess.codemap.lookup_char_pos(span.lo);
}
fn size_and_align_of(cx: @mut CrateContext, t: ty::t) -> (uint, uint) {
let llty = type_of::type_of(cx, t);
(machine::llsize_of_real(cx, llty), machine::llalign_of_min(cx, llty))
}
#[inline]
fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext {
cx.dbg_cx.get_mut_ref()
}
#[inline]
fn DIB(cx: &CrateContext) -> DIBuilderRef {
cx.dbg_cx.get_ref().builder
}