Closed
Description
I run two test functions, each on its own thread. Both of these function test the same functionality.
As result each line of tested code should executed two times.
But time to time the execution counter for lines are different.
My code
#include <string>
#include <string_view>
#include <cassert>
#include <thread>
struct State {
std::string data;
static State parse(std::string_view src) { return State{std::string(src)}; }
bool has(std::string_view needle) const {
return this->data == needle;
}
};
void state_has_true() {
const auto s = State::parse("value");
assert(s.has("value"));
}
void state_has_false() {
const auto s = State::parse("value");
assert(!s.has("missing"));
}
int main() {
std::thread t1(state_has_true);
std::thread t2(state_has_false);
t1.join();
t2.join();
}
If I run code coverage calculation only once, then all work as expected:
$ clang++ -std=c++17 -Wall -Wextra -pedantic -fprofile-instr-generate -fcoverage-mapping foo.cc -o foo
$ LLVM_PROFILE_FILE="foo.profraw" ./foo
$ llvm-profdata merge -sparse foo.profraw -o foo.profdata
$ llvm-cov show ./foo -instr-profile=foo.profdata | grep -E '^ ( 9|1[1-3])'
9| 2| static State parse(std::string_view src) { return State{std::string(src)}; }
11| 2| bool has(std::string_view needle) const {
12| 2| return this->data == needle;
13| 2| }
But if I run such script test.sh
, that repeats shell code above 15000 times,
then I certainly got error:
#!/bin/bash
set -euo pipefail
make
export LLVM_PROFILE_FILE="foo.profraw"
for i in $(seq 1 15000); do
rm -f $LLVM_PROFILE_FILE foo.profdata
./foo
llvm-profdata merge -sparse foo.profraw -o foo.profdata
if [ ! -z "$(llvm-cov show ./foo -instr-profile=foo.profdata | grep -E '^ ( 9|1[1-3])' | grep -v '| 2|')" ]; then
echo "Found BUG at step $i"
llvm-cov show ./foo -instr-profile=foo.profdata
exit 1
fi
done
Result of running the script above:
❯ sh test.sh
Found BUG at step 460
1| |#include <string>
2| |#include <string_view>
3| |#include <cassert>
4| |#include <thread>
5| |
6| |struct State {
7| | std::string data;
8| |
9| 2| static State parse(std::string_view src) { return State{std::string(src)}; }
10| |
11| 1| bool has(std::string_view needle) const {
12| 1| return this->data == needle;
13| 1| }
14| |};
15| |
16| 1|void state_has_true() {
17| 1| const auto s = State::parse("value");
18| 1| assert(s.has("value"));
19| 1|}
20| |
21| 1|void state_has_false() {
22| 1| const auto s = State::parse("value");
23| 1| assert(!s.has("missing"));
24| 1|}
25| |
26| 1|int main() {
27| 1| std::thread t1(state_has_true);
28| 1| std::thread t2(state_has_false);
29| |
30| |
31| 1| t1.join();
32| 1| t2.join();
33| 1|}
As you see counters for lines 11-13 are equal 1 while should be 2.