@@ -8,8 +8,8 @@ use crate::args::ConcretePlaybackMode;
8
8
use crate :: call_cbmc:: VerificationStatus ;
9
9
use crate :: cbmc_output_parser:: VerificationOutput ;
10
10
use crate :: session:: KaniSession ;
11
- use anyhow:: { ensure , Context , Result } ;
12
- use concrete_vals_extractor:: { extract_from_processed_items , ConcreteVal } ;
11
+ use anyhow:: { Context , Result } ;
12
+ use concrete_vals_extractor:: { extract_harness_values , ConcreteVal } ;
13
13
use kani_metadata:: HarnessMetadata ;
14
14
use std:: collections:: hash_map:: DefaultHasher ;
15
15
use std:: ffi:: OsString ;
@@ -26,54 +26,52 @@ impl KaniSession {
26
26
harness : & HarnessMetadata ,
27
27
verification_output : & VerificationOutput ,
28
28
) -> Result < ( ) > {
29
- let playback_mode = match & self . args . concrete_playback {
29
+ let playback_mode = match self . args . concrete_playback {
30
30
Some ( playback_mode) => playback_mode,
31
31
None => return Ok ( ( ) ) ,
32
32
} ;
33
33
34
- ensure ! (
35
- self . args. output_format != crate :: args:: OutputFormat :: Old ,
36
- "The Kani argument `--output-format old` is not supported with the concrete playback feature."
37
- ) ;
38
-
39
34
if verification_output. status == VerificationStatus :: Success {
40
35
return Ok ( ( ) ) ;
41
36
}
42
37
43
38
if let Some ( processed_items) = & verification_output. processed_items {
44
- let concrete_vals = extract_from_processed_items ( processed_items) . expect (
45
- "Something went wrong when trying to get concrete values from the CBMC output" ,
46
- ) ;
47
- let concrete_playback = format_unit_test ( & harness. mangled_name , & concrete_vals) ;
48
-
49
- if * playback_mode == ConcretePlaybackMode :: Print {
50
- ensure ! (
51
- !self . args. quiet,
52
- "With `--quiet` mode enabled, `--concrete-playback=print` mode can not print test cases."
53
- ) ;
54
- println ! (
55
- "Concrete playback unit test for `{}`:\n ```\n {}\n ```" ,
56
- & harness. mangled_name, & concrete_playback. unit_test_str
57
- ) ;
58
- println ! (
59
- "INFO: To automatically add the concrete playback unit test `{}` to the src code, run Kani with `--concrete-playback=InPlace`." ,
60
- & concrete_playback. unit_test_name
61
- ) ;
62
- }
63
-
64
- if * playback_mode == ConcretePlaybackMode :: InPlace {
65
- if !self . args . quiet {
66
- println ! (
67
- "INFO: Now modifying the source code to include the concrete playback unit test `{}`." ,
68
- & concrete_playback. unit_test_name
69
- ) ;
39
+ match extract_harness_values ( processed_items) {
40
+ None => println ! (
41
+ "WARNING: Kani could not produce a concrete playback for `{}` because there \
42
+ were no failing panic checks.",
43
+ harness. pretty_name
44
+ ) ,
45
+ Some ( concrete_vals) => {
46
+ let concrete_playback = format_unit_test ( & harness. mangled_name , & concrete_vals) ;
47
+ match playback_mode {
48
+ ConcretePlaybackMode :: Print => {
49
+ println ! (
50
+ "Concrete playback unit test for `{}`:\n ```\n {}\n ```" ,
51
+ & harness. pretty_name, & concrete_playback. unit_test_str
52
+ ) ;
53
+ println ! (
54
+ "INFO: To automatically add the concrete playback unit test `{}` to the \
55
+ src code, run Kani with `--concrete-playback=inplace`.",
56
+ & concrete_playback. unit_test_name
57
+ ) ;
58
+ }
59
+ ConcretePlaybackMode :: InPlace => {
60
+ if !self . args . quiet {
61
+ println ! (
62
+ "INFO: Now modifying the source code to include the concrete playback unit test `{}`." ,
63
+ & concrete_playback. unit_test_name
64
+ ) ;
65
+ }
66
+ self . modify_src_code (
67
+ & harness. original_file ,
68
+ harness. original_end_line ,
69
+ & concrete_playback,
70
+ )
71
+ . expect ( "Failed to modify source code" ) ;
72
+ }
73
+ }
70
74
}
71
- self . modify_src_code (
72
- & harness. original_file ,
73
- harness. original_end_line ,
74
- & concrete_playback,
75
- )
76
- . expect ( "Failed to modify source code" ) ;
77
75
}
78
76
}
79
77
Ok ( ( ) )
@@ -290,89 +288,53 @@ struct UnitTest {
290
288
/// ..., ] }
291
289
/// ```
292
290
mod concrete_vals_extractor {
293
- use crate :: cbmc_output_parser:: {
294
- extract_property_class, CheckStatus , ParserItem , Property , TraceItem ,
295
- } ;
296
- use anyhow:: { bail, ensure, Context , Result } ;
291
+ use crate :: cbmc_output_parser:: { extract_property_class, CheckStatus , ParserItem , TraceItem } ;
297
292
298
293
pub struct ConcreteVal {
299
294
pub byte_arr : Vec < u8 > ,
300
295
pub interp_val : String ,
301
296
}
302
297
303
- /// The first-level extractor. Traverses processed items to find properties.
304
- pub fn extract_from_processed_items (
305
- processed_items : & [ ParserItem ] ,
306
- ) -> Result < Vec < ConcreteVal > > {
307
- let mut concrete_vals: Vec < ConcreteVal > = Vec :: new ( ) ;
308
- let mut extracted_assert_fail = false ;
309
- let result_item = extract_result_from_processed_items ( processed_items) ?;
310
- for property in result_item {
311
- // Even after extracting an assert fail, we continue to call extract on more properties to provide
312
- // better diagnostics to the user in case they expected even future checks to be extracted.
313
- let old_extracted_assert_fail = extracted_assert_fail;
314
- let new_concrete_vals = extract_from_property ( property, & mut extracted_assert_fail) ?;
315
- if !old_extracted_assert_fail && extracted_assert_fail {
316
- concrete_vals = new_concrete_vals;
317
- }
318
- }
319
- Ok ( concrete_vals)
320
- }
321
-
322
- /// Extracts the result item from all the processed items. No result item means that there is an error.
323
- fn extract_result_from_processed_items ( processed_items : & [ ParserItem ] ) -> Result < & [ Property ] > {
324
- for processed_item in processed_items {
325
- if let ParserItem :: Result { result } = processed_item {
326
- return Ok ( result) ;
327
- }
328
- }
329
- bail ! ( "No result item found in processed items." )
330
- }
331
-
332
- /// The second-level extractor. Traverses properties to find trace items.
333
- fn extract_from_property (
334
- property : & Property ,
335
- extracted_assert_fail : & mut bool ,
336
- ) -> Result < Vec < ConcreteVal > > {
337
- let mut concrete_vals: Vec < ConcreteVal > = Vec :: new ( ) ;
338
- let property_class =
339
- extract_property_class ( property) . context ( "Incorrectly formatted property class." ) ?;
340
- let property_is_assert = property_class == "assertion" ;
341
- let status_is_failure = property. status == CheckStatus :: Failure ;
342
-
343
- if property_is_assert && status_is_failure {
344
- if * extracted_assert_fail {
298
+ /// Extract a set of concrete values that trigger one assertion failure.
299
+ /// This will return None if the failure is not related to a user assertion.
300
+ pub fn extract_harness_values ( processed_items : & [ ParserItem ] ) -> Option < Vec < ConcreteVal > > {
301
+ let result_item =
302
+ processed_items
303
+ . iter ( )
304
+ . find_map ( |item| {
305
+ if let ParserItem :: Result { result } = item { Some ( result) } else { None }
306
+ } )
307
+ . expect ( "Missing CBMC result." ) ;
308
+
309
+ let mut failures = result_item. iter ( ) . filter ( |prop| {
310
+ extract_property_class ( prop) . expect ( "Unexpected property class." ) == "assertion"
311
+ && prop. status == CheckStatus :: Failure
312
+ } ) ;
313
+
314
+ // Process the first assertion failure.
315
+ let first_failure = failures. next ( ) ;
316
+ if let Some ( property) = first_failure {
317
+ // Extract values for the first assertion that has failed.
318
+ let trace =
319
+ property. trace . as_ref ( ) . expect ( & format ! ( "Missing trace for {}" , property. property) ) ;
320
+ let concrete_vals = trace. iter ( ) . filter_map ( & extract_from_trace_item) . collect ( ) ;
321
+
322
+ // Print warnings for all the other failures that were not handled in case they expected
323
+ // even future checks to be extracted.
324
+ for unhandled in failures {
345
325
println ! (
346
326
"WARNING: Unable to extract concrete values from multiple failing assertions. Skipping property `{}` with description `{}`." ,
347
- property. property, property. description,
348
- ) ;
349
- } else {
350
- * extracted_assert_fail = true ;
351
- println ! (
352
- "INFO: Parsing concrete values from property `{}` with description `{}`." ,
353
- property. property, property. description,
327
+ unhandled. property, unhandled. description,
354
328
) ;
355
- if let Some ( trace) = & property. trace {
356
- for trace_item in trace {
357
- let concrete_val_opt = extract_from_trace_item ( trace_item)
358
- . context ( "Failure in trace assignment expression:" ) ?;
359
- if let Some ( concrete_val) = concrete_val_opt {
360
- concrete_vals. push ( concrete_val) ;
361
- }
362
- }
363
- }
364
329
}
365
- } else if !property_is_assert && status_is_failure {
366
- println ! (
367
- "WARNING: Unable to extract concrete values from failing non-assertion checks. Skipping property `{}` with description `{}`." ,
368
- property. property, property. description,
369
- ) ;
330
+ Some ( concrete_vals)
331
+ } else {
332
+ None
370
333
}
371
- Ok ( concrete_vals)
372
334
}
373
335
374
- /// The third-level extractor. Extracts individual bytes from kani::any calls.
375
- fn extract_from_trace_item ( trace_item : & TraceItem ) -> Result < Option < ConcreteVal > > {
336
+ /// Extracts individual bytes returned by kani::any() calls.
337
+ fn extract_from_trace_item ( trace_item : & TraceItem ) -> Option < ConcreteVal > {
376
338
if let ( Some ( lhs) , Some ( source_location) , Some ( value) ) =
377
339
( & trace_item. lhs , & trace_item. source_location , & trace_item. value )
378
340
{
@@ -389,37 +351,33 @@ mod concrete_vals_extractor {
389
351
{
390
352
let declared_width = width_u64 as usize ;
391
353
let actual_width = bit_concrete_val. len ( ) ;
392
- ensure ! (
393
- declared_width == actual_width,
394
- format!(
395
- "Declared width of {declared_width} doesn't equal actual width of {actual_width}"
396
- )
354
+ assert_eq ! (
355
+ declared_width, actual_width,
356
+ "Declared width of {declared_width} doesn't equal actual width of {actual_width}"
397
357
) ;
398
358
let mut next_num: Vec < u8 > = Vec :: new ( ) ;
399
359
400
360
// Reverse because of endianess of CBMC trace.
401
361
for i in ( 0 ..declared_width) . step_by ( 8 ) . rev ( ) {
402
362
let str_chunk = & bit_concrete_val[ i..i + 8 ] ;
403
363
let str_chunk_len = str_chunk. len ( ) ;
404
- ensure ! (
405
- str_chunk_len == 8 ,
406
- format!(
407
- "Tried to read a chunk of 8 bits of actually read {str_chunk_len} bits"
408
- )
364
+ assert_eq ! (
365
+ str_chunk_len, 8 ,
366
+ "Tried to read a chunk of 8 bits of actually read {str_chunk_len} bits"
409
367
) ;
410
- let next_byte = u8:: from_str_radix ( str_chunk, 2 ) . with_context ( || {
411
- format ! ( "Couldn't convert the string chunk `{str_chunk}` to u8" )
412
- } ) ? ;
368
+ let next_byte = u8:: from_str_radix ( str_chunk, 2 ) . expect ( & format ! (
369
+ "Couldn't convert the string chunk `{str_chunk}` to u8"
370
+ ) ) ;
413
371
next_num. push ( next_byte) ;
414
372
}
415
373
416
- return Ok ( Some ( ConcreteVal {
374
+ return Some ( ConcreteVal {
417
375
byte_arr : next_num,
418
376
interp_val : interp_concrete_val. to_string ( ) ,
419
- } ) ) ;
377
+ } ) ;
420
378
}
421
379
}
422
380
}
423
- Ok ( None )
381
+ None
424
382
}
425
383
}
0 commit comments