]>
Commit | Line | Data |
---|---|---|
d9579d0f AL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use self::InternalDebugLocation::*; | |
12 | ||
13 | use super::utils::{debug_context, span_start, fn_should_be_ignored}; | |
14 | use super::metadata::{scope_metadata,UNKNOWN_COLUMN_NUMBER}; | |
15 | use super::{FunctionDebugContext, DebugLoc}; | |
16 | ||
17 | use llvm; | |
18 | use llvm::debuginfo::DIScope; | |
19 | use trans::common::{NodeIdAndSpan, CrateContext, FunctionContext}; | |
20 | ||
21 | use libc::c_uint; | |
22 | use std::ptr; | |
23 | use syntax::codemap::{Span, Pos}; | |
24 | use syntax::{ast, codemap}; | |
25 | ||
26 | pub fn get_cleanup_debug_loc_for_ast_node<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, | |
27 | node_id: ast::NodeId, | |
28 | node_span: Span, | |
29 | is_block: bool) | |
30 | -> NodeIdAndSpan { | |
31 | // A debug location needs two things: | |
32 | // (1) A span (of which only the beginning will actually be used) | |
33 | // (2) An AST node-id which will be used to look up the lexical scope | |
34 | // for the location in the functions scope-map | |
35 | // | |
36 | // This function will calculate the debug location for compiler-generated | |
37 | // cleanup calls that are executed when control-flow leaves the | |
38 | // scope identified by `node_id`. | |
39 | // | |
40 | // For everything but block-like things we can simply take id and span of | |
41 | // the given expression, meaning that from a debugger's view cleanup code is | |
42 | // executed at the same source location as the statement/expr itself. | |
43 | // | |
44 | // Blocks are a special case. Here we want the cleanup to be linked to the | |
45 | // closing curly brace of the block. The *scope* the cleanup is executed in | |
46 | // is up to debate: It could either still be *within* the block being | |
47 | // cleaned up, meaning that locals from the block are still visible in the | |
48 | // debugger. | |
49 | // Or it could be in the scope that the block is contained in, so any locals | |
50 | // from within the block are already considered out-of-scope and thus not | |
51 | // accessible in the debugger anymore. | |
52 | // | |
53 | // The current implementation opts for the second option: cleanup of a block | |
54 | // already happens in the parent scope of the block. The main reason for | |
55 | // this decision is that scoping becomes controlflow dependent when variable | |
56 | // shadowing is involved and it's impossible to decide statically which | |
57 | // scope is actually left when the cleanup code is executed. | |
58 | // In practice it shouldn't make much of a difference. | |
59 | ||
60 | let mut cleanup_span = node_span; | |
61 | ||
62 | if is_block { | |
63 | // Not all blocks actually have curly braces (e.g. simple closure | |
64 | // bodies), in which case we also just want to return the span of the | |
65 | // whole expression. | |
66 | let code_snippet = cx.sess().codemap().span_to_snippet(node_span); | |
67 | if let Ok(code_snippet) = code_snippet { | |
68 | let bytes = code_snippet.as_bytes(); | |
69 | ||
70 | if !bytes.is_empty() && &bytes[bytes.len()-1..] == b"}" { | |
71 | cleanup_span = Span { | |
72 | lo: node_span.hi - codemap::BytePos(1), | |
73 | hi: node_span.hi, | |
74 | expn_id: node_span.expn_id | |
75 | }; | |
76 | } | |
77 | } | |
78 | } | |
79 | ||
80 | NodeIdAndSpan { | |
81 | id: node_id, | |
82 | span: cleanup_span | |
83 | } | |
84 | } | |
85 | ||
86 | ||
87 | /// Sets the current debug location at the beginning of the span. | |
88 | /// | |
89 | /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id | |
90 | /// parameter is used to reliably find the correct visibility scope for the code | |
91 | /// position. | |
92 | pub fn set_source_location(fcx: &FunctionContext, | |
93 | node_id: ast::NodeId, | |
94 | span: Span) { | |
95 | match fcx.debug_context { | |
96 | FunctionDebugContext::DebugInfoDisabled => return, | |
97 | FunctionDebugContext::FunctionWithoutDebugInfo => { | |
98 | set_debug_location(fcx.ccx, UnknownLocation); | |
99 | return; | |
100 | } | |
101 | FunctionDebugContext::RegularContext(box ref function_debug_context) => { | |
102 | if function_debug_context.source_location_override.get() { | |
103 | // Just ignore any attempts to set a new debug location while | |
104 | // the override is active. | |
105 | return; | |
106 | } | |
107 | ||
108 | let cx = fcx.ccx; | |
109 | ||
110 | debug!("set_source_location: {}", cx.sess().codemap().span_to_string(span)); | |
111 | ||
112 | if function_debug_context.source_locations_enabled.get() { | |
113 | let loc = span_start(cx, span); | |
114 | let scope = scope_metadata(fcx, node_id, span); | |
115 | ||
116 | set_debug_location(cx, InternalDebugLocation::new(scope, | |
117 | loc.line, | |
118 | loc.col.to_usize())); | |
119 | } else { | |
120 | set_debug_location(cx, UnknownLocation); | |
121 | } | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
126 | /// This function makes sure that all debug locations emitted while executing | |
127 | /// `wrapped_function` are set to the given `debug_loc`. | |
128 | pub fn with_source_location_override<F, R>(fcx: &FunctionContext, | |
129 | debug_loc: DebugLoc, | |
130 | wrapped_function: F) -> R | |
131 | where F: FnOnce() -> R | |
132 | { | |
133 | match fcx.debug_context { | |
134 | FunctionDebugContext::DebugInfoDisabled => { | |
135 | wrapped_function() | |
136 | } | |
137 | FunctionDebugContext::FunctionWithoutDebugInfo => { | |
138 | set_debug_location(fcx.ccx, UnknownLocation); | |
139 | wrapped_function() | |
140 | } | |
141 | FunctionDebugContext::RegularContext(box ref function_debug_context) => { | |
142 | if function_debug_context.source_location_override.get() { | |
143 | wrapped_function() | |
144 | } else { | |
145 | debug_loc.apply(fcx); | |
146 | function_debug_context.source_location_override.set(true); | |
147 | let result = wrapped_function(); | |
148 | function_debug_context.source_location_override.set(false); | |
149 | result | |
150 | } | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | /// Clears the current debug location. | |
156 | /// | |
157 | /// Instructions generated hereafter won't be assigned a source location. | |
158 | pub fn clear_source_location(fcx: &FunctionContext) { | |
159 | if fn_should_be_ignored(fcx) { | |
160 | return; | |
161 | } | |
162 | ||
163 | set_debug_location(fcx.ccx, UnknownLocation); | |
164 | } | |
165 | ||
166 | /// Enables emitting source locations for the given functions. | |
167 | /// | |
168 | /// Since we don't want source locations to be emitted for the function prelude, | |
169 | /// they are disabled when beginning to translate a new function. This functions | |
170 | /// switches source location emitting on and must therefore be called before the | |
171 | /// first real statement/expression of the function is translated. | |
172 | pub fn start_emitting_source_locations(fcx: &FunctionContext) { | |
173 | match fcx.debug_context { | |
174 | FunctionDebugContext::RegularContext(box ref data) => { | |
175 | data.source_locations_enabled.set(true) | |
176 | }, | |
177 | _ => { /* safe to ignore */ } | |
178 | } | |
179 | } | |
180 | ||
181 | ||
182 | #[derive(Copy, Clone, PartialEq)] | |
183 | pub enum InternalDebugLocation { | |
184 | KnownLocation { scope: DIScope, line: usize, col: usize }, | |
185 | UnknownLocation | |
186 | } | |
187 | ||
188 | impl InternalDebugLocation { | |
189 | pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { | |
190 | KnownLocation { | |
191 | scope: scope, | |
192 | line: line, | |
193 | col: col, | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | pub fn set_debug_location(cx: &CrateContext, debug_location: InternalDebugLocation) { | |
199 | if debug_location == debug_context(cx).current_debug_location.get() { | |
200 | return; | |
201 | } | |
202 | ||
203 | let metadata_node; | |
204 | ||
205 | match debug_location { | |
206 | KnownLocation { scope, line, .. } => { | |
207 | // Always set the column to zero like Clang and GCC | |
208 | let col = UNKNOWN_COLUMN_NUMBER; | |
209 | debug!("setting debug location to {} {}", line, col); | |
210 | ||
211 | unsafe { | |
212 | metadata_node = llvm::LLVMDIBuilderCreateDebugLocation( | |
213 | debug_context(cx).llcontext, | |
214 | line as c_uint, | |
215 | col as c_uint, | |
216 | scope, | |
217 | ptr::null_mut()); | |
218 | } | |
219 | } | |
220 | UnknownLocation => { | |
221 | debug!("clearing debug location "); | |
222 | metadata_node = ptr::null_mut(); | |
223 | } | |
224 | }; | |
225 | ||
226 | unsafe { | |
227 | llvm::LLVMSetCurrentDebugLocation(cx.raw_builder(), metadata_node); | |
228 | } | |
229 | ||
230 | debug_context(cx).current_debug_location.set(debug_location); | |
231 | } |