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) {
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 /* */ macro_rules! from {
97 /* */ ($from:expr) => { &format!("from: {}\n to: {}:{}:{}", $from, file!(), line!(), column!()) };
100 /* */ #[derive(Debug)]
101 /* */ enum TestEnum {
107 /* */ struct TestStruct {
111 /* */ // IMPORTANT! IS WRAPPING main() ENOUGH? OR DO I ALSO NEED TO WRAP THREAD FUNCTIONS, ASSUMING
112 /* */ // THEY ARE STILL RUNNING WITH MAIN EXITS? (IF THEY CAN). NOT SURE HOW RUST HANDLES THAT.
114 /* */ // I SUSPECT USING THREAD_LOCAL COUNTERS MAY NOT ACTUALLY BE AN OPTIMIZATION OVER MUTEX LOCKS,
115 /* */ // BUT MAYBE I SHOULD ASK.
117 /* */ impl TestStruct {
118 /* - */ fn new() -> Self {
119 /* ┃ */ let __result = Self::new_with_value(31415); // function-scoped counter index = 0
120 /* ┃ */ __incr_cov(from!("fn new()"));
124 /* - */ fn new_with_value(field: i32) -> Self {
125 /* ┃ */ let __result = Self {
128 /* ┃ */ __incr_cov(from!("fn new_with_value()")); // function-scoped counter index = 0
132 /* */ fn call_closure<F>(&self, closure: F) -> bool
138 /* ┃ */ let __result = closure(123);
139 /* ┃ */ __incr_cov(from!("fn call_closure()")); // function-scoped counter index = 0
143 /* - */ fn various(&self) -> Result<(),Error> {
144 /* ┃ */ use TestEnum::*;
145 /* ┃ */ let mut color = Red;
146 /* ┃ */ let _ = color;
147 /* ┃ */ color = Blue;
148 /* ┃ */ let _ = color;
149 /* ┃ */ color = Green;
150 /* ┃ */ match { let __result = color; __incr_cov(from!("fn various")); __result } { // function-scoped counter index = 0
152 /* : */ // !!! RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK (THE FUNCTION IN THIS CASE) TO END OF MATCH EXPRESSION
153 /* : */ // If `match`, `while`, `loop`, `for`, `if`, etc. expression has a `return`, `break`, or `continue`
154 /* : */ // (if legal), then RECORD SPAN FROM START OF INNERMOST CONTAINING BLOCK TO END OF `return` EXPRESSION
155 /* : */ // If the expression includes lazy booleans, nest calls to `__incr_cov()`.
156 /* : I */ Red => {println!("roses"); __incr_cov(from!("Red => or end of MatchArmGuard expression inside pattern, if any"));}
158 /* : ┃ */ let spidey = 100;
159 /* : ┃ */ let goblin = 50;
160 /* : ┃ */ // if spidey > goblin {__incr_cov(from!(""),{
161 /* : ┃ */ // println!("what ev");
163 /* : ┃ */ // ACTUALLY, WRAPPING THE ENTIRE IF BLOCK IN `__incr_cov` IS NOT A GREAT GENERAL RULE.
164 /* : ┃ */ // JUST INSERTING A `return`, `break`, or `continue` IN THAT BLOCK (without an intermediate condition)
165 /* : ┃ */ // MAKES THE `__incr_cov()` CALL UNREACHABLE!
166 /* : ┃ */ // MY ORIGINAL SOLUTION WORKS BETTER (WRAP LAST EXPRESSION OR AFTER LAST SEMICOLON STATEMENT IN BLOCK)
167 /* : ┃ */ // UNLESS THE EXPRESSION IS NOT A BLOCK.
168 /* : ┃ - */ if { let __result = spidey > goblin; __incr_cov(from!("Green => or end of MatchArmGuard expression inside pattern, if any")); __result } {
169 /* : : ┃ */ println!("spidey beats goblin");
170 /* : : ┃ */ __incr_cov(from!("block start"));
171 /* : ┃ - */ } else if { let __result = spidey == goblin; __incr_cov(from!("`else if` on this line")); __result } {
172 /* : : ┃ */ // COVERAGE NOTE: Do we mark only the expression span (that may be trivial, as in this case),
173 /* : : ┃ */ // or associate it with the outer block, similar to how the `if` expression is associated with
174 /* : : ┃ */ // the outer block? (Although it is a continuation, in a sense, it is discontiguous in this case,
175 /* : : ┃ */ // so I think simpler to just make it its own coverage region.)
176 /* : : ┃ */ println!("it's a draw");
177 /* : : ┃ */ __incr_cov(from!("block start"));
178 /* : ┃ - - - */ } else if if { let __result = true; __incr_cov(from!("`else if` on this line")); __result } {
179 /* : : : ┃ */ // return __incr_cov(from!("after `if true`"),Ok(()));
180 /* : : : ┃ */ // ACTUALLY, BECAUSE OF `return`, WE DO NOT RECORD THE `if true` EVEN THOUGH WE COVERED IT.
181 /* : : : ┃ */ // IN FACT, IF THIS NESTED CONDITIONAL IN A CONDITIONAL EXPRESSION WAS AN `if` (WITHOUT PRECEDING ELSE)
182 /* : : : ┃ */ // WE WOULD NOT HAVE RECORDED THE COVERAGE OF STATEMENTS LEADING UP TO THE `if`, SO
183 /* : : : ┃ */ // IT SHOULD BE:
184 /* ┏-:---:-------:---< */ return { let __result = Ok(()); __incr_cov(from!("")); __result };
185 /* V : : : : */ // NOTE THE `from` STRING IS SAME FOR THE `else if`s `__incr_cov` AND THIS `return`.
186 /* : : : : */ // ONLY ONE OF THESE WILL EXECUTE, TO RECORD COVERAGE FROM THAT SPOT.
187 /* : : ┃ - */ } else {
188 /* : : : I */ { let __result = false; __incr_cov(from!("`else`")); __result }
190 /* : : ┃ */ println!("wierd science");
191 /* : : ┃ */ __incr_cov(from!("block start"));
193 /* : : ┃ */ println!("goblin wins");
194 /* ┏-:---:---< */ return { let __result = Ok(()); __incr_cov(from!("`else`")); __result }; // THIS COUNTS LAST STATEMENT IN `else` BLOCK
195 /* V : : : */ // COVERAGE NOTE: When counting the span for `return`,
196 /* : : : */ // `break`, or `continue`, also report the outer spans
197 /* : : : */ // got this far--including this `else` block. Record
198 /* : : : */ // The start positions for those outer blocks, but:
199 /* : : : */ // * For the block containing the `return`, `break`, or
200 /* : : : */ // `continue`, end report the end position is the
201 /* : : : */ // start of the `return` span (or 1 char before it).
202 /* : : : */ // * Anything else?
204 /* : ┃ - */ // __incr_cov(from!("")); // DO NOT COUNT HERE IF NO STATEMENTS AFTER LAST `if` or `match`
206 /* : I */ Blue => { println!("violets"); __incr_cov(from!("Blue => or end of MatchArmGuard expression inside pattern, if any")); }
209 /* ┃ */ let condition1 = true;
210 /* ┃ */ let condition2 = false;
211 /* ┃ */ let condition3 = true;
213 /* ┃ */ println!("Called `various()` for TestStruct with field={}", self.field);
215 /* ┃ - */ if { let __result = condition1; __incr_cov(from!("after block end of prior `match` (or `if-else if-else`)")); __result } {
216 /* : ┃ */ println!("before for loop");
217 /* : ┃ - */ for index in { let __result = 0..10; __incr_cov(from!("block start")); __result } {
218 /* : : ┃ */ println!("top of `for` loop");
219 /* : : ┃ - */ if { let __result = index == 8; __incr_cov(from!("block start")); __result } {
220 /* : : : ┃ */ println!("before break");
221 /* : : : ┃ */ // note the following is not legal here:
222 /* : : : ┃ */ // "can only break with a value inside `loop` or breakable block"
223 /* : : : ┃ */ // break __incr_cov(from!(""));
224 /* : : : ┃ */ __incr_cov(from!("block start"));
225 /* : : ┏-----< */ break;
227 /* : : : : */ // FIXME(richkadel): add examples with loop labels, breaking out of inner and outer loop to outer loop label, with expression.
228 /* : : : : */ // May want to record both the span and the start position after the broken out block depdnding on label
230 /* : : ┃ */ println!("after `break` test");
231 /* : : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `if index == 8`")); __result } {
232 /* ┏-:---:---:---< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result };
235 /* : : ┃ */ // BECAUSE THE PREVIOUS COVERAGE REGION HAS A `return`, THEN
236 /* : : ┃ */ // IF PREVIOUS COVERAGE REGION IS NOT COUNTED THEN OUTER REGION REACHED HERE.
237 /* : : ┃ */ // ADD A COVERAGE REGION FOR THE SPAN FROM JUST AFTER PREVIOUS REGION TO END
238 /* : : ┃ */ // OF OUTER SPAN, THEN TRUNCATE TO NEXT REGION NOT REACHED.
239 /* : : ┃ - */ if index % 3 == 2 { // NO __incr_cov() HERE BECAUSE NO STATEMENTS BETWEEN LAST CONDITIONAL BLOCK AND START OF THIS ONE
240 /* : : Λ : ┃ */ __incr_cov(from!("block end of `if condition2`"));
241 /* : : ┗-----< */ continue;
243 /* : : ┃ */ println!("after `continue` test");
244 /* : : ┃ */ // maybe add a runtime flag for a possible `return` here?
245 /* : : ┃ */ __incr_cov(from!(""));
247 /* : ┃ */ println!("after for loop");
248 /* : ┃ */ let result = if { // START OF NEW CONDITIONAL EXPRESSION. NEXT "GUARANTEED" COUNTER SHOULD COUNT FROM END OF LAST CONDITIONAL EXPRESSION
249 /* : ┃ */ // A "GUARANTEED" COUNTER CALL IS ONE THAT WILL BE CALLED REGARDLESS OF OTHER CONDITIONS. THIS INCLUDES:
250 /* : ┃ */ // * A CONDITIONAL EXPRESSION THAT IS NOT A BLOCK (OR ANOTHER CONDITIONAL STATEMENT, WHICH WOULD CONTAIN A BLOCK)
251 /* : ┃ */ // * OR IF THE NEXT CONDITIONAL EXPRESSION IS A BLOCK OR CONDITIONAL STATEMENT, THEN THE FIRST "GUARANTEED" COUNTER IN THAT BLOCK
252 /* : ┃ */ // * END OF BLOCK IF THE BLOCK DOES NOT HAVE INNER CONDITIONAL EXPRESSIONS
253 /* : ┃ */ // * BRANCHING STATEMENTS (`return`, `break`, `continue`) BY EITHER WRAPPING THE BRANCH STATEMENT NON-BLOCK EXPRESSION,
254 /* : ┃ */ // OR PREPENDING A COUNTER WITH EMPTY TUPLE IF NO EXPRESSION, OR IF EXPRESSION IS A BLOCK, THEN THE NEXT "GUARANTEED"
255 /* : ┃ */ // COUNTER CALL WITHIN THAT BLOCK.
256 /* : ┃ */ // BASICALLY, CARRY THE START OF COVERAGE SPAN FORWARD UNTIL THE GUARANTEED COUNTER IS FOUND
257 /* : ┃ */ println!("after result = if ...");
258 /* : ┃ - */ if { let __result = condition2; __incr_cov(from!("block end of `for` loop")); __result } {
259 /* : : ┃ */ println!("before first return");
260 /* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result };
261 /* V : : - */ } else if { let __result = condition3; __incr_cov(from!("`else`")); __result } {
262 /* : : ┃ */ // THE ABOVE COUNTER IS _NOT_ REALLY NECESSARY IF EXPRESSION IS GUARANTEED TO EXECUTE.
263 /* : : ┃ */ // IF WE GET COUNTER IN `else if` BLOCK WE COVERED EXPRESSION.
264 /* : : ┃ */ // IF WE GET TO ANY REMAINING `else` or `else if` BLOCK WE KNOW WE EVALUATED THIS CONDITION
265 /* : : ┃ */ // AND ALL OTHERS UP TO THE EXECUTED BLOCK. BUT THE SPAN WOULD HAVE "HOLES" FOR UNEXECUTED BLOCKS.
266 /* : : ┃ */ println!("not second return");
267 /* ┏-:---:-------< */ return { let __result = Ok(()); __incr_cov(from!("block start")); __result };
268 /* V : : - */ } else {
269 /* : : ┃ */ println!("not returning");
270 /* : : ┃ */ { let __result = false; __incr_cov(from!("block start")); __result }
272 /* : ┃ */ // NO COUNTER HERE BECAUSE NO STATEMENTS AFTER CONDITIONAL BLOCK
274 /* : : ┃ */ println!("branched condition returned true");
275 /* : : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result }
276 /* : ┃ - */ } else if self.call_closure(
277 /* : : - */ |closure_param| {
278 /* : : ┃ - */ let __result = if condition3 {
279 /* : : : ┃ */ println!("in closure, captured condition said to print the param {}", closure_param);
280 /* : : : ┃ */ { let __result = false; __incr_cov(from!("")); __result }
281 /* : : ┃ - */ } else {
282 /* : : : ┃ */ println!("in closure, captured condition was false");
283 /* : : : ┃ */ { let __result = true; __incr_cov(from!("")); __result }
285 /* : : - */ __incr_cov(from!("")); __result }
287 /* : : ┃ */ println!("closure returned true");
288 /* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned true")); __incr_cov(from!("")); __result }
290 /* : : ┃ */ println!("closure returned false");
291 /* : : ┃ */ { let __result = Err(Error::new(ErrorKind::Other, "Result is error if closure returned false")); __incr_cov(from!("")); __result }
293 /* : ┃ */ println!("bottom of function might be skipped if early `return`");
294 /* : ┃ */ { let __result = result; __incr_cov(from!("if condition1")); __result }
296 /* : ┃ */ println!("skipping everything in `various()`");
297 /* : ┃ */ { let __result = Ok(()); __incr_cov(from!("")); __result }
299 /* ┃ - */ // __incr_cov(from!(""),0) // DO NOT COUNT IF NO STATEMENTS AFTER CONDITIONAL BLOCK. ALL COVERAGE IS ALREADY COUNTED
303 /* - */ fn main() -> Result<(), std::io::Error> {
304 /* ┃ */ //let mut status: u8 = 2;
305 /* ┃ */ let mut status: u8 = 1;
306 /* : - */ let result = if status < 2 &&
307 /* : ┃ */ { let __result = {
308 /* : ┃ */ status -= 1;
309 /* : ┃ */ status == 0
310 /* : - - */ }; __incr_cov(from!("")); __result } {
311 /* : ┃ */ let test_struct = TestStruct::new_with_value(100);
312 /* : ┃ */ let _ = test_struct.various();
313 /* ┏-:---< */ return { let __result = Err(Error::new(ErrorKind::Other, format!("Error status {}", status))); __incr_cov(from!("")); __report(); __result }
315 /* : ┃ */ let test_struct = TestStruct::new();
316 /* : ┃ */ { let __result = test_struct.various(); __incr_cov(from!("")); __result }
318 /* ┃ */ println!("done");
319 /* ┃ */ { let __result = result; __incr_cov(from!("")); __report(); __result }