C++ SDK tutorial: times10 function#

You are a 10x programmer and will soon have your annual performance review, so you are writing a function that takes any number and multiplies it by ten. That way if anybody names some productivity, you can respond with your ten times greater productivity.

You write the function in C++ and at first you use print debugging. However, this performance review is very important for your career so you decide not to take any chances. You ultimately decide to thoroughly test this function using the Antithesis C++ SDK.

The rest of the document walks you through this process.

Note

Recall that the basic SDK workflow is

  1. Include the C++ header file.

  2. Use SDK functions in your code.

  3. Compile and link your code.

  4. Deploy your build.

Creating your project#

Step 0: Write your project. You begin by writing your function in C++ with printf debugging.

Make a directory where you would like to create your C++ project and cd to that directory.

mkdir myapp
cd myapp

Create a file myapp.cpp with the following content:

myapp.cpp#
 1  #include <cstdio>
 2
 3  static int times10(int x) {
 4      int result = x * 10;
 5      return result;
 6  }
 7
 8  int main() {
 9      printf("Hello, world!\n");
10      printf("%d x 10 = %d\n", 3, times10(3));
11      printf("%d x 10 = %d\n", 8, times10(8));
12  }

Now build and run. For convenience, this is displayed as two separate steps: compiling and linking.

clang++ -o myapp.o -c myapp.cpp
clang++ -o myapp_executable myapp.o
./myapp_executable

and it prints

Hello, world!
3 x 10 = 30
8 x 10 = 80

Using our SDK functions#

Step 1: Include the C++ header file:

If this is your first time using the Antithesis C++ SDK, make a directory for it and clone it from github.

mkdir ~/src/github
pushd ~/src/github
git clone git@github.com:antithesishq/antithesis-sdk-cpp.git
popd

Include the SDK header file and the instrumentation header file:

Step 2. Use SDK functions in your code

You wrote a function to multiply numbers by ten. What sorts of properties should it have? The output should always be even. You should also make sure you are testing a wide variety of inputs, such as both even and odd numbers.

Modify the code. The modifications are individually explained below.

myapp.cpp#
 1  #include <cstdio>
 2  #include "antithesis_sdk.h"
 3  #include "antithesis_instrumentation.h"
 4
 5  static int times10(int x) {
 6      SOMETIMES(x % 2 == 1, "input is sometimes odd", {{"input", x}});
 7      int result = x * 10;
 8      ALWAYS(result % 2 == 0, "result is always even", {{"result", result}});
 9      return result;
10  }
11
12  int main() {
13      printf("Hello, world!\n");
14      for (int i = 0; i < 50; i++) {
15          int x = int(antithesis::get_random() % 500);
16          printf("%d x 10 = %d\n", x, times10(x));
17      }
18  }
  • Lines 2-3 You imported the Antithesis C++ SDK and Antithesis C++ coverage instrumentation.

  • You added two assertions to times10.

    • Line 8 You assert that the result is even using an Always Assertion. This is a fairly conventional assertion.

    • Line 6 You insert a Sometimes Assertion, or an assertion that something will happen at least once across the entire testing session. You assert that sometimes during testing, the function will be called with an odd argument.

      The two types of assertions complement one another: Always Assertions assert that the behavior is correct, whereas Sometimes Assertions assert that you are testing under wide enough conditions to surface any potential incorrect behavior. In this case, the output would trivially be even if you only passed it even inputs—you must ensure your properties are not being trivially satisfied!

  • Lines 14-17 You use randomness to call the function with many random values (between 0 and 500). Previously, you called the function with hardcoded values but now you will pass the function random values and test that the output is always even. This approach is more powerful but makes Sometimes Assertions necessary—now you must test that you are passing the function odd values, whereas previously the tests were hardcoded so you were certain that you were passing it odd values.

    You call the random function get_random to draw a random number, and then pass this random number to times10. You use a loop to do this fifty times in a row. Every time the times10 function is called in this loop, it triggers the assertions in lines 5 & 7. In summary: You will test times10 by passing it a random integer fifty times in a row. You have asserted that all fifty outputs must be even and that at least one random input must be odd.

You will now compile and instrument your project.

Building your project#

To send code to Antithesis, you should compile with coverage instrumentation. This guide adds instrumentation as well as demonstrates using the SDK.

Step 3: Build your code—compiling

Change the compile commandline as follows:

1  clang++ \
2      -I ~/src/github/antithesis-sdk-cpp/ \
3      -std=c++20 \
4      -fsanitize-coverage=trace-pc-guard \
5      -g \
6      -o myapp.o \
7      -c myapp.cpp
  • Line 2 adds the include path for the Antithesis C++ SDK.

  • Line 3 uses C++ 20, which is required to use the SDK.

  • Line 4 adds instrumentation

  • Line 5 turns on (optional) debug symbols.

Step 3: Build your code—linking

Link your code:

1  clang++ \
2      -Wl,--build-id \
3      -o myapp_executable \
4      myapp.o
  • Lines 1,3, and 4 are unchanged from the previous link command.

  • Line 2 sends the --build-id flag to the linker

4. Deploy your build

You are now ready to build your project. If you are building for local deployment then you would be done here, but suppose instead you intend to send your project to Antithesis for testing. Antithesis will explore your software and search for violations of the properties you have defined.

You must send us containers as described in getting started.

  • The executable should be included in your container. In this example, myapp/myapp_executable.

  • The instrumentation symbols () should be included in your configuration image. In this case, we’ve generated an unstripped binary, so we’ll just include myapp/myapp_executable in the configuration image, at the same path where we included the executable in the runnable container.

Note

This example is simplified compared to what the full Getting Started guide describes. Note that the function main is the workload and times10 is the software itself. There are no dependencies. The instrumentation symbols will go in the configuration image as stated.

Ultimately, you will receive a triage report that confirms the properties you defined are true: the output of the function is always even and the inputs to the function are sometimes odd. You now have a well-tested function that can multiply numbers by ten.