Skip to content
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

Coverage bytecode filter #335

Merged
merged 9 commits into from
Jun 15, 2024
Merged

Coverage bytecode filter #335

merged 9 commits into from
Jun 15, 2024

Conversation

dmitrii-artuhov
Copy link
Collaborator


Notice

This PR also changes the way classes bytecode is restore to the initially loaded into Lincheck agent. Instead of redefineClasses(...) now retransformClasses(...) is used in order to avoid double-instrumentation by other attached agents (e.g. intellij-coverage agent was throwing ClassFormatError because of this problem).


Description

Relates to #254.

This PR adds a bytecode filter class, which "hides" bytecode generated by intellij-coverage agent (e.g. when using kover gradle plugin) from transformation that exist in Lincheck.

  • Hide bytecode generated by default kover settings (hits[index] = 1)
  • Hide bytecode generated by default "Run with coverage" settings in Intellij IDEA (hits[index] += 1)

The bytecode tracking is implemented as state machine with 5 states (+ symbol designates the direction of an arrow):

                           INITIAL  +----------------------------------------
                       /            +                                       |
                     /                \                                     |
                   +                    +                                   |
          HITS_INIT           HITS_INIT_FIELD                               |
                    \                    /                                  |
                     +                  +                                   |
                        HITS_IN_LOCAL   -------------------------------------  
                         |         +                
                         +         |                
                      HITS_BEFORE_ASSIGN 

Short overview:

  • The state of automata is reset when starting to instrument a new method
  • HITS_INIT and HITS_INIT_FIELD states occur, when the hits array (which stores coverage data) is loaded onto stack. The difference between state is the bytecode that they target: the 1st one occurs when hits was loaded via LDC instruction, the 2nd when GETSTATIC instruction was used.
  • The way intellij coverage loads hits using LDC is as follows:
    LDC __$hits$__ : Ljava/lang/Object; ...
    CHECKCAST [Z
    ASTORE #n
    and using the GETSTATIC:
    GETSTATIC CLAZZ.__$hits$__ : [Z
    IFNONNULL L0
    ...
    INVOKESTATIC com/intellij/rt/coverage/instrumentation/CoverageRuntime.getHitsMask (Ljava/lang/String;)[Z
    PUTSTATIC CLAZZ.__$hits$__ : [Z
    L0
    GETSTATIC CLAZZ.__$hits$__ : [Z
    ASTORE #n
    Here GETSTATIC is called twice, that is why on the diagram above there is a double-sided arrow between INITIAL and LOADED_ON_STACK_STATIC states (first time hits is loaded on stack to check if it is null, then it is discarded, and lastly it is put on stack once again).
  • HITS_IN_LOCAL occurs when the hits array is already saved to local variables of the method and the automata knows its index.
  • When the ALOAD #n instruction occurs automata goes into HITS_BEFORE_ASSIGN state, then it searches for bytecode of hits[index] = 1 or/and hits[index] += 1. When the assignment completed the state goes back to HITS_IN_LOCAL.
  • When the method is fully transformed, the automata is put to INITIAL state again.

@dmitrii-artuhov dmitrii-artuhov requested a review from eupp June 14, 2024 13:47
Copy link
Collaborator

@eupp eupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good, thank you!

Please address the last minor comment, and then it is ready to be merged!

@eupp eupp mentioned this pull request Jun 14, 2024
@eupp eupp merged commit 8c3ed5d into develop Jun 15, 2024
15 checks passed
@eupp eupp deleted the coverage-bytecode-filter branch June 15, 2024 12:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants