Skip to content

Commit a13c4ab

Browse files
committedMar 28, 2023
Fold tutorial into readme.md and move readme into top-level docs
1 parent 850e941 commit a13c4ab

File tree

3 files changed

+114
-117
lines changed

3 files changed

+114
-117
lines changed
 

‎src/libcprover-rust/readme.md

+113-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# CProver (CBMC) Rust API
1+
# Libcprover-rust
22

3-
This folder contains the implementation of the Rust API of the CProver (CBMC) project.
3+
A Rust interface for convenient interaction with the CProver tools.
44

55
## Building instructions
66

@@ -41,6 +41,117 @@ $ cargo clean
4141
$ CBMC_INCLUDE_DIR=../../build/include CBMC_LIB_DIR=../../build/lib CBMC_VERSION=5.78.0 cargo test -- --test-threads=1 --nocapture
4242
```
4343

44+
## Basic Usage
45+
46+
This file will guide through a sample interaction with the API, under a basic
47+
scenario: *loading a file and verifying the model contained within*.
48+
49+
To begin, we will assume that you have a file under `/tmp/api_example.c`,
50+
with the following contents:
51+
52+
```c
53+
int main(int argc, char *argv[])
54+
{
55+
int arr[] = {0, 1, 2, 3};
56+
__CPROVER_assert(arr[3] != 3, "expected failure: arr[3] == 3");
57+
}
58+
```
59+
60+
The first thing we need to do to initiate any interaction with the API
61+
itself is to create a new `api_sessiont` handle by using the function
62+
`new_api_session`:
63+
64+
```rust
65+
let client = cprover_api::new_api_session();
66+
```
67+
68+
Then, we need to add the file to a vector with filenames that indicate
69+
which files we want the verification engine to load the models of:
70+
71+
```rust
72+
let vec: Vec<String> = vec!["/tmp/api_example.c".to_owned()];
73+
74+
let vect = ffi_util::translate_rust_vector_to_cpp(vec);
75+
```
76+
77+
In the above code example, we created a Rust language Vector of Strings
78+
(`vec`). In the next line, we called a utility function from the module
79+
`ffi_util` to translate the Rust `Vec<String>` into the C++ equivalent
80+
`std::vector<std::string>` - this step is essential, as we need to translate
81+
the type into something that the C++ end understands.
82+
83+
These operations are *explicit*: we have opted to force users to translate
84+
between types at the FFI level in order to reduce the "magic" and instill
85+
mental models more compatible with the nature of the language-border (FFI)
86+
work. If we didn't, and we assumed the labour of translating these types
87+
transparently at the API level, we risked mistakes from our end or from the
88+
user end frustrating debugging efforts.
89+
90+
At this point, we have a handle of a C++ vector containing the filenames
91+
of the files we want the CProver verification engine to load. To do so,
92+
we're going to use the following piece of code:
93+
94+
```rust
95+
// Invoke load_model_from_files and see if the model has been loaded.
96+
if let Err(_) = client.load_model_from_files(vect) {
97+
eprintln!("Failed to load model from files: {:?}", vect);
98+
process::exit(1);
99+
}
100+
```
101+
102+
The above is an example of a Rust idiom known as a `if let` - it's effectively
103+
a pattern match with just one pattern - we don't match any other case.
104+
105+
What we we do above is two-fold:
106+
107+
* We call the function `load_model_from_files` with the C++ vector (`vect`)
108+
we prepared before. It's worth noting that this function is being called
109+
with `client.` - what this does is that it passes the `api_session` handle
110+
we initialised at the beginning as the first argument to the `load_model_from_files`
111+
on the C++ API's end.
112+
* We handled the case where the model loading failed for whatever reason from
113+
the C++ end by catching the error on the Rust side and printing a suitable error
114+
message and exiting the process gracefully.
115+
116+
---
117+
118+
*Interlude*: **Error Handling**
119+
120+
`cxx.rs` (the FFI bridge we're using to build the Rust API) allows for a mechanism
121+
wherein exceptions from the C++ program can be translated into Rust `Result<>` types
122+
provided suitable infrastructure has been built.
123+
124+
Our Rust API contains a C++ shim which (among other things) intercepts CProver
125+
exceptions (from `cbmc`, etc.) and translates them into a form that the bridge
126+
can then translate to appropriate `Result` types that the Rust clients can use.
127+
128+
This means that, as above, we can use the same Rust idioms and types as we would
129+
use on a purely Rust based codebase to interact with the API.
130+
131+
*All of the API calls* are returning `Result` types such as above.
132+
133+
---
134+
135+
After we have loaded the model, we can proceed to then engage the verification
136+
engine for an analysis run:
137+
138+
```rust
139+
if let Err(_) = client.verify_model() {
140+
eprintln!("Failed to verify model from files: {:?}", vect);
141+
process::exit(1);
142+
}
143+
```
144+
145+
While all this is happening, we are collecting the output of the various
146+
phases into a message buffer. We can go forward and print any messages from
147+
that buffer into `stdout`:
148+
149+
```rust
150+
let msgs_cpp = cprover_api::get_messages();
151+
let msgs_rust = ffi_util::translate_cpp_vector_to_rust(msgs_cpp);
152+
ffi_util::print_response(msgs_rust);
153+
```
154+
44155
## Notes
45156

46157
* The functions supported by the Rust API are catalogued within the `ffi` module within

‎src/libcprover-rust/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![doc = include_str!("../tutorial.md")]
1+
#![doc = include_str!("../readme.md")]
22
#![warn(missing_docs)]
33

44
/// The main API module for interfacing with CProver tools (`cbmc`, `goto-analyzer`, etc).

‎src/libcprover-rust/tutorial.md

-114
This file was deleted.

0 commit comments

Comments
 (0)