1 /* */ use std::io::Error;
2 /* */ use std::io::ErrorKind;
4 /* */ /// Align Rust counter increment with with:
5 /* */ /// [‘llvm.instrprof.increment’ Intrinsic](https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic)
7 /* */ /// declare void @llvm.instrprof.increment(i8* <name>, i64 <hash>, i32 <num-counters>, i32 <index>)
9 /* */ /// The first argument is a pointer to a global variable containing the name of the entity
10 /* */ /// being instrumented. This should generally be the (mangled) function name for a set of
13 /* */ /// The second argument is a hash value that can be used by the consumer of the profile data
14 /* */ /// to detect changes to the instrumented source, and the third is the number of counters
15 /* */ /// associated with name. It is an error if hash or num-counters differ between two
16 /* */ /// instances of instrprof.increment that refer to the same name.
18 /* */ /// The last argument refers to which of the counters for name should be incremented. It
19 /* */ /// should be a value between 0 and num-counters.
23 /* */ /// `mangled_fn_name` - &'static ref to computed and injected static str, using:
26 /* */ /// fn rustc_symbol_mangling::compute_symbol_name(
27 /* */ /// tcx: TyCtxt<'tcx>,
28 /* */ /// instance: Instance<'tcx>,
29 /* */ /// compute_instantiating_crate: impl FnOnce() -> CrateNum,
33 /* */ /// `source_version_hash` - Compute hash based that only changes if there are "significant"
34 /* */ /// to control-flow inside the function.
36 /* */ /// `num_counters` - The total number of counter calls [MAX(counter_index) + 1] within the
39 /* */ /// `counter_index` - zero-based counter index scoped by the function. (Ordering of
40 /* */ /// counters, relative to the source code location, is apparently not expected.)
44 /* */ /// * The mangled_fn_name may not be computable until generics are monomorphized (see
45 /* */ /// parameters required by rustc_symbol_mangling::compute_symbol_name).
46 /* */ /// * The version hash may be computable from AST analysis, and may not benefit from further
48 /* */ /// * num_counters depends on having already identified all counter insertion locations.
49 /* */ /// * counter_index can be computed at time of counter insertion (incrementally).
50 /* */ /// * Numeric parameters are signed to match the llvm increment intrinsic parameter types.
51 /* */ fn __lower_incr_cov(_mangled_fn_name: &'static str, _fn_version_hash: i64, _num_counters: i32, _counter_index: i32) {
54 /* */ /// A coverage counter implementation that will work as both an intermediate coverage
55 /* */ /// counting and reporting implementation at the AST-level only--for debugging and
56 /* */ /// development--but also serves as a "marker" to be replaced by calls to LLVM
57 /* */ /// intrinsic coverage counter APIs during the lowering process.
59 /* */ /// Calls to this function will be injected automatically into the AST. When LLVM intrinsics
60 /* */ /// are enabled, the counter function calls that were injected into the AST serve as
61 /* */ /// placeholders, to be replaced by an alternative, such as:
63 /* */ /// * direct invocation of the `llvm.instrprof.increment()` intrinsic; or
64 /* */ /// * the `__lower_incr_cov()` function, defined above, that would invoke the
65 /* */ /// `llvm.instrprof.increment()` intrinsic; or
66 /* */ /// * a similar expression wrapper, with the additional parameters (as defined above
67 /* */ /// for `__lower_incr_cov()`, that invokes `llvm.instrprof.increment()` and returns the
68 /* */ /// result of the wrapped expression)
70 /* */ /// The first two options would require replacing the inlined wrapper call with something
74 /* */ /// { let result = {expr}; __inlined_incr_cov(context, counter); result }
77 /* */ /// But if the lowering process is already unwrapping the inlined call to `__incr_cov()`, then
78 /* */ /// it may be a perfect opportunity to replace the function with one of these more
79 /* */ /// direct methods.
81 /* */ #[inline(always)]
82 /* */ pub fn __incr_cov(region_loc: &str, /*index: u32,*/) {
83 /* */ // Either call the intermediate non-llvm coverage counter API or
84 /* */ // replace the call to this function with the expanded `__lower_incr_cov()` call.
86 /* */ // let _lock = increment_counter(counter);
87 /* */ println!("{}", region_loc);
90 /* */ /// Write a report identifying each incremented counter and the number of times each counter
91 /* */ /// was incremented.
93 /* */ println!("WRITE REPORT!");
96 /* */ /// Increment the counter after evaluating the wrapped expression (see `__incr_cov()`), then
97 /* */ /// write a report identifying each incremented counter and the number of times each counter
98 /* */ /// was incremented.
99 /* */ #[inline(always)]
100 /* */ pub fn __incr_cov_and_report<T>(region_loc: &str, /*counter: u32,*/ result: T) -> T {
101 /* */ __incr_cov(region_loc, /*counter,*/);
106 /* */ macro_rules! from {
107 /* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) };
110 /* */ macro_rules! to {
111 /* */ ($to:expr) => { &format!("to: {}\n to: {}:{}:{}", $to, file!(), line!(), column!()) };
114 /* */ #[derive(Debug)]
115 /* */ enum TestEnum {
121 /* */ struct TestStruct {
125 /* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING
126 /* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT.
128 /* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS,
129 /* */ // BUT MAYBE I SHOULD ASK.
131 /* */ impl TestStruct {
132 /* - */ fn new() -> Self {
133 /* ┃ */ __incr_cov(to!("end of fn new()")); // function-scoped counter index = 0
134 /* ┃ */ Self::new_with_value(31415)
137 /* - */ fn new_with_value(field: i32) -> Self {
138 /* ┃ */ __incr_cov(to!("end of fn new_with_value()")); // function-scoped counter index = 0
144 /* */ fn call_closure<F>(&self, closure: F) -> bool
150 /* ┃ */ __incr_cov(to!("end of fn call_closure()")); // function-scoped counter index = 0
154 /* - */ fn various(&self) -> Result<(),Error> {
155 /* ┃ */ __incr_cov(to!("just before next branch: after `match color`: pattern selection"));
156 /* ┃ */ use TestEnum::*;
157 /* ┃ */ let mut color = Red;
158 /* ┃ */ let _ = color;
159 /* ┃ */ color = Blue;
160 /* ┃ */ let _ = color;
161 /* ┃ */ color = Green;
162 /* ┃ */ match color { // function-scoped counter index = 0
164 /* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION
165 /* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue`
166 /* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION
167 /* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`.
169 /* : ┃ */ __incr_cov(to!("end of matched Red"));
170 /* : ┃ */ println!("roses");
173 /* : ┃ */ __incr_cov(to!("just before next branch: after `if spidey > goblin`"));
174 /* : ┃ */ let spidey = 100;
175 /* : ┃ */ let goblin = 50;
176 /* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{
177 /* : ┃ */ // println!("what ev");
179 /* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE.
180 /* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition)
181 /* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE!
182 /* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK)
183 /* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK.
184 /* : ┃ - */ if spidey > goblin {
185 /* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope"));
186 /* : : ┃ */ println!("spidey beats goblin");
188 /* : ┃ - */ } else if {
189 /* : : : */ // Make sure we can't compute the coverage count here.
190 /* : : : */ // We know the expression executed if the previous if block DID NOT
191 /* : : : */ // execute, and either this `else if` block does execute OR any subsequent
192 /* : : : */ // `else if` or `else` blocks execute, OR none of the blocks in the
193 /* : : : */ // `if`, `else if` or `else` blocks execute.
194 /* : : : */ // `if`, `else if` or `else` blocks execute.
195 /* : : ┃ */ __incr_cov(to!("end of `else if spidey == goblin` expression"));
196 /* : : ┃ */ spidey == goblin
198 /* : : ┃ */ __incr_cov(to!("end of if block, if no earlier branch in this scope"));
199 /* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case),
200 /* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with
201 /* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case,
202 /* : : ┃ */ // so I think simpler to just make it its own coverage region.)
203 /* : : ┃ */ println!("it's a draw");
205 /* : ┃ - - - */ } else if {
206 /* : : ┃ */ __incr_cov(to!("end of `if true`"));
207 /* : ┃ - - - */ if true {
208 /* : : : ┃ */ __incr_cov(to!("end of `return Ok(())`"));
209 /* ┏-:---:-------:---< */ return Ok(());
210 /* V : : ┃ - */ } else {
211 /* : : : ┃ */ // __incr_cov(to!("end of else block"));
212 /* : : : ┃ */ // computed counter expression
216 /* : : ┃ */ __incr_cov(to!("end of if block"));
217 /* : : ┃ */ println!("wierd science");
219 /* : : ┃ */ // __incr_cov(to!("end of `return Ok(())"));
220 /* : : ┃ */ // counter expression: (start of Green match arm) - (if spidey > goblin) - (previous `} else if {`)
221 /* : : ┃ */ println!("goblin wins");
222 /* ┏-:---:---< */ return Ok(()); // THIS COUNTS LAST STATEMENT IN `else` BLOCK
223 /* V : : : */ // COVERAGE NOTE: When counting the span for `return`,
224 /* : : : */ // `break`, or `continue`, also report the outer spans
225 /* : : : */ // got this far--including this `else` block. Record
226 /* : : : */ // The start positions for those outer blocks, but:
227 /* : : : */ // * For the block containing the `return`, `break`, or
228 /* : : : */ // `continue`, end report the end position is the
229 /* : : : */ // start of the `return` span (or 1 char before it).
230 /* : : : */ // * Anything else?
232 /* : : */ // __incr_cov(to!("end of matched Green"));
233 /* : : */ // // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match`
236 /* : ┃ */ __incr_cov(to!("end of matched Blue"));
237 /* : ┃ */ println!("violets");
240 /* ┃ */ __incr_cov(to!("just before next branch: after `if condition1` (HIR: 'match condition1')"));
242 /* ┃ */ let condition1 = true;
243 /* ┃ */ let condition2 = false;
244 /* ┃ */ let condition3 = true;
246 /* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field);
248 /* ┃ - */ if condition1 {
249 /* : ┃ */ println!("before while loop");
250 /* : ┃ */ let mut countdown = 10;
251 /* : ┃ */ // Must increment before repeated while text expression
252 /* : : I */ while countdown > 0 { // span is just the while test expression
253 /* : : ┃ */ println!("top of `while` loop");
254 /* : : ┃ */ countdown -= 1;
255 /* : : ┃ */ // // Counter not needed, but span is computed as "while test" minus "block start"
256 /* : : ┃ */ // If test expression is 11, and the outer block runs only once, 11-1 = 10
258 /* : ┃ */ println!("before for loop");
259 /* : ┃ - */ for index in 0..10 {
260 /* : : ┃ */ println!("top of `for` loop");
261 /* : : ┃ - */ if index == 8 {
262 /* : : : ┃ */ println!("before break");
263 /* : : : ┃ */ // note the following is not legal here:
264 /* : : : ┃ */ // "can only break with a value inside `loop` or breakable block"
265 /* : : : ┃ */ // break
267 /* : : ┏-----< */ break;
269 /* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression.
270 /* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label
272 /* : : ┃ */ println!("after `break` test");
273 /* : : ┃ - */ if condition2 {
274 /* ┏-:---:---:---< */ return Ok(());
277 /* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN
278 /* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE.
279 /* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END
280 /* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED.
281 /* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE
283 /* : : ┗-----< */ continue;
285 /* : : ┃ */ println!("after `continue` test");
286 /* : : ┃ */ // maybe add a runtime flag for a possible `return` here?
289 /* : ┃ */ println!("after for loop");
290 /* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION
291 /* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES:
292 /* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK)
293 /* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK
294 /* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS
295 /* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION,
296 /* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED"
297 /* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK.
298 /* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND
299 /* : ┃ */ println!("after result = if ...");
300 /* : ┃ - */ if condition2 {
301 /* : : ┃ */ println!("before first return");
302 /* ┏-:---:-------< */ return Ok(());
303 /* V : : - */ } else if condition3 {
304 /* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE.
305 /* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION.
306 /* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION
307 /* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS.
308 /* : : ┃ */ println!("not second return");
309 /* ┏-:---:-------< */ return Ok(());
310 /* V : : - */ } else {
311 /* : : ┃ */ println!("not returning");
314 /* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK
316 /* : : ┃ */ println!("branched condition returned true");
318 /* : ┃ - */ } else if self.call_closure(
319 /* : : - */ |closure_param|
320 /* : : ┃ - */ if condition3 {
321 /* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param);
323 /* : : ┃ - */ } else {
324 /* : : : ┃ */ println!("in closure, captured condition was false");
329 /* : : ┃ */ println!("closure returned true");
330 /* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned true"))
332 /* : : ┃ */ println!("closure returned false");
333 /* : : ┃ */ Err(Error::new(ErrorKind::Other, "Result is error if closure returned false"))
335 /* : ┃ */ println!("bottom of function might be skipped if early `return`");
338 /* : ┃ */ println!("skipping everything in `various()`");
341 /* ┃ - */ // 0 // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED
345 /* - */ fn main() -> Result<(), std::io::Error> {
346 /* ┃ */ //let mut status: u8 = 2;
347 /* ┃ */ let mut status: u8 = 1;
348 /* : - */ let result = if status < 2 &&
350 /* : ┃ */ status -= 1;
351 /* : ┃ */ status == 0
353 /* : ┃ */ let test_struct = TestStruct::new_with_value(100);
354 /* : ┃ */ let _ = test_struct.various();
355 /* ┏-:---< */ return __incr_cov_and_report(from!(""),Err(Error::new(ErrorKind::Other, format!("Error status {}", status))))
357 /* : ┃ */ let test_struct = TestStruct::new();
358 /* : ┃ */ test_struct.various()
360 /* ┃ */ println!("done");
361 /* ┃ */ __incr_cov_and_report(from!(""),result) // function-scoped counter index = 0