Skip to content

debuginfo: Improvements for stepping experience in GDB #10966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 16, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/librustc/middle/trans/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ use util::common::indenter;
use util::ppaux::{Repr, vec_map_to_str};

use std::hashmap::HashMap;
use std::ptr;
use std::vec;
use syntax::ast;
use syntax::ast::Ident;
Expand Down Expand Up @@ -2046,7 +2047,10 @@ pub fn store_arg(mut bcx: @mut Block,
// Debug information (the llvm.dbg.declare intrinsic to be precise) always expects to get an
// alloca, which only is the case on the general path, so lets disable the optimized path when
// debug info is enabled.
let fast_path = !bcx.ccx().sess.opts.extra_debuginfo && simple_identifier(pat).is_some();
let arg_is_alloca = unsafe { llvm::LLVMIsAAllocaInst(llval) != ptr::null() };

let fast_path = (arg_is_alloca || !bcx.ccx().sess.opts.extra_debuginfo)
&& simple_identifier(pat).is_some();

if fast_path {
// Optimized path for `x: T` case. This just adopts
Expand Down
25 changes: 22 additions & 3 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,11 @@ pub fn trans_external_path(ccx: &mut CrateContext, did: ast::DefId, t: ty::t) ->
}
}

pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef],
attributes: &[(uint, lib::llvm::Attribute)])
pub fn invoke(bcx: @mut Block,
llfn: ValueRef,
llargs: ~[ValueRef],
attributes: &[(uint, lib::llvm::Attribute)],
call_info: Option<NodeInfo>)
-> (ValueRef, @mut Block) {
let _icx = push_ctxt("invoke_");
if bcx.unreachable {
Expand All @@ -899,11 +902,18 @@ pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef],
}
}
let normal_bcx = sub_block(bcx, "normal return");
let landing_pad = get_landing_pad(bcx);

match call_info {
Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span),
None => debuginfo::clear_source_location(bcx.fcx)
};

let llresult = Invoke(bcx,
llfn,
llargs,
normal_bcx.llbb,
get_landing_pad(bcx),
landing_pad,
attributes);
return (llresult, normal_bcx);
} else {
Expand All @@ -913,6 +923,12 @@ pub fn invoke(bcx: @mut Block, llfn: ValueRef, llargs: ~[ValueRef],
debug!("arg: {}", llarg);
}
}

match call_info {
Some(info) => debuginfo::set_source_location(bcx.fcx, info.id, info.span),
None => debuginfo::clear_source_location(bcx.fcx)
};

let llresult = Call(bcx, llfn, llargs, attributes);
return (llresult, bcx);
}
Expand Down Expand Up @@ -1551,6 +1567,7 @@ pub fn alloca_maybe_zeroed(cx: @mut Block, ty: Type, name: &str, zero: bool) ->
return llvm::LLVMGetUndef(ty.ptr_to().to_ref());
}
}
debuginfo::clear_source_location(cx.fcx);
let p = Alloca(cx, ty, name);
if zero {
let b = cx.fcx.ccx.builder();
Expand All @@ -1567,6 +1584,7 @@ pub fn arrayalloca(cx: @mut Block, ty: Type, v: ValueRef) -> ValueRef {
return llvm::LLVMGetUndef(ty.to_ref());
}
}
debuginfo::clear_source_location(cx.fcx);
return ArrayAlloca(cx, ty, v);
}

Expand Down Expand Up @@ -1810,6 +1828,7 @@ pub fn finish_fn(fcx: @mut FunctionContext, last_bcx: @mut Block) {
None => last_bcx
};
build_return_block(fcx, ret_cx);
debuginfo::clear_source_location(fcx);
fcx.cleanup();
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ pub fn trans_call_inner(in_cx: @mut Block,
}

// Invoke the actual rust fn and update bcx/llresult.
let (llret, b) = base::invoke(bcx, llfn, llargs, attrs);
let (llret, b) = base::invoke(bcx, llfn, llargs, attrs, call_info);
bcx = b;
llresult = llret;

Expand Down
80 changes: 61 additions & 19 deletions src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,43 @@ continuation, storing all state needed to continue traversal at the type members
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.

*/


Expand Down Expand Up @@ -651,7 +688,16 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
(function_name.clone(), file_metadata)
};

let scope_line = get_scope_line(cx, top_level_block, loc.line);
// 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;

// 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.
let is_local_to_unit = !cx.reachable.contains(&fn_ast_id);

let fn_metadata = function_name.with_c_str(|function_name| {
linkage_name.with_c_str(|linkage_name| {
Expand All @@ -664,7 +710,7 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
file_metadata,
loc.line as c_uint,
function_type_metadata,
false,
is_local_to_unit,
true,
scope_line as c_uint,
FlagPrototyped as c_uint,
Expand All @@ -687,6 +733,9 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
let arg_pats = fn_decl.inputs.map(|arg_ref| arg_ref.pat);
populate_scope_map(cx, arg_pats, top_level_block, fn_metadata, &mut fn_debug_context.scope_map);

// Clear the debug location so we don't assign them in the function prelude
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the key piece that makes sure we handle the __morestack prelude correctly? It's probably worth expanding this comment to cover why we need to handle this specially (and at least a mention of __morestack so that there is no ambiguity about what "function prelude" means).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me if you add comments

On Sat, Dec 14, 2013 at 7:56 AM, Huon Wilson [email protected]:

In src/librustc/middle/trans/debuginfo.rs:

@@ -687,6 +689,9 @@ pub fn create_function_debug_context(cx: &mut CrateContext,
let arg_pats = fn_decl.inputs.map(|arg_ref| arg_ref.pat);
populate_scope_map(cx, arg_pats, top_level_block, fn_metadata, &mut fn_debug_context.scope_map);

  • // Clear the debug location so we don't assign them in the function prelude

Is this the key piece that makes sure we handle the __morestack prelude
correctly? It's probably worth expanding this comment to cover why we
need to handle this specially (and at least a mention of __morestack so
that there is no ambiguity about what "function prelude" means).


Reply to this email directly or view it on GitHubhttps://github.com//pull/10966/files#r8351606
.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the key piece that makes sure we handle the __morestack prelude correctly?

It's necessary for any kind of function, with and without segmented stack. It's probably best if I add a section about function prologues and line information at the beginning of the file, since the functionality spans across multiple functions here...

But it's definitely a good idea to add a bit of explanation on these topics. Thanks for you suggestions, Huon!

set_debug_location(cx, UnknownLocation);

return FunctionDebugContext(fn_debug_context);

fn get_function_signature(cx: &mut CrateContext,
Expand Down Expand Up @@ -837,21 +886,6 @@ pub fn create_function_debug_context(cx: &mut CrateContext,

return create_DIArray(DIB(cx), template_params);
}

fn get_scope_line(cx: &CrateContext,
top_level_block: &ast::Block,
default: uint)
-> uint {
match *top_level_block {
ast::Block { stmts: ref statements, .. } if statements.len() > 0 => {
span_start(cx, statements[0].span).line
}
ast::Block { expr: Some(@ref expr), .. } => {
span_start(cx, expr.span).line
}
_ => default
}
}
}

//=-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -2128,7 +2162,8 @@ fn set_debug_location(cx: &mut CrateContext, debug_location: DebugLocation) {
let metadata_node;

match debug_location {
KnownLocation { scope, line, col } => {
KnownLocation { scope, line, .. } => {
let col = 0; // Always set the column to zero like Clang and GCC
debug!("setting debug location to {} {}", line, col);
let elements = [C_i32(line as i32), C_i32(col as i32), scope, ptr::null()];
unsafe {
Expand Down Expand Up @@ -2244,7 +2279,14 @@ fn populate_scope_map(cx: &mut CrateContext,
})
}

walk_block(cx, fn_entry_block, &mut scope_stack, scope_map);
// 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,
scope_map,
|cx, scope_stack, scope_map| {
walk_block(cx, fn_entry_block, scope_stack, scope_map);
});

// local helper functions for walking the AST.
fn with_new_scope(cx: &mut CrateContext,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/glue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ pub fn trans_struct_drop(bcx: @mut Block, t: ty::t, v0: ValueRef, dtor_did: ast:
add_clean(bcx, llfld_a, fld.mt.ty);
}

let (_, bcx) = invoke(bcx, dtor_addr, args, []);
let (_, bcx) = invoke(bcx, dtor_addr, args, [], None);
bcx
})
}
Expand Down
3 changes: 1 addition & 2 deletions src/test/debug-info/basic-types-metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@
// debugger:whatis f64
// check:type = f64
// debugger:info functions _yyy
// check:[...]
// check:![...]_yyy()();
// check:[...]![...]_yyy()();
// debugger:detach
// debugger:quit

Expand Down
Loading