Skip to content

Data race in Source-based Code Coverage #62558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Dushistov opened this issue May 4, 2023 · 1 comment
Closed

Data race in Source-based Code Coverage #62558

Dushistov opened this issue May 4, 2023 · 1 comment
Labels
coverage worksforme Resolved as "works for me"

Comments

@Dushistov
Copy link

Dushistov commented May 4, 2023

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.

@Dushistov
Copy link
Author

The problem solved by clang flag -fprofile-update=atomic

@EugeneZelenko EugeneZelenko added the worksforme Resolved as "works for me" label May 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
coverage worksforme Resolved as "works for me"
Projects
None yet
Development

No branches or pull requests

2 participants