fun install()

in jvm-agent/src/main/org/jetbrains/lincheck/jvm/agent/LincheckJavaAgent.kt [204:268]


    fun install(instrumentationMode: InstrumentationMode) {
        this.instrumentationMode = instrumentationMode
        setInstrumentationStrategy()
        // The bytecode injections must be loaded with the bootstrap class loader,
        // as the `java.base` module is loaded with it. To achieve that, we pack the
        // classes related to the bytecode injections in a separate JAR (see the
        // "bootstrap" project module), and add it to the bootstrap classpath.
        if (!isBootstrapJarAddedToClasspath) { // don't do this twice.
            appendBootstrapJarToClassLoaderSearch()
            isBootstrapJarAddedToClasspath = true
        }
        // Add the Lincheck bytecode transformer to this JVM instance,
        // allowing already loaded classes re-transformation.
        instrumentation.addTransformer(LincheckClassFileTransformer, true)
        // The transformation logic depends on the testing strategy.
        when {
            // In the stress testing mode, we use an additional optimization.
            // In this mode, Lincheck needs to track only the coroutine suspensions.
            // As an optimization, we remember the set of loaded classes that actually
            // have suspension points, so later we can re-transform only those classes.
            instrumentationMode == STRESS -> {
                // If `INSTRUMENT_ALL_CLASSES` is explicitly set ---
                // disable the optimization and re-transform all classes
                if (INSTRUMENT_ALL_CLASSES) {
                    retransformClasses(getLoadedClassesToInstrument())
                    return
                }
                // Perform optimized re-transformation.
                check(instrumentedClasses.isEmpty())
                val classes = getLoadedClassesToInstrument().filter {
                    val canonicalClassName = it.name
                    // new classes that were loaded after the latest STRESS mode re-transformation
                    !transformedClassesCache.containsKey(canonicalClassName) ||
                    // old classes that were already loaded before and have coroutine method calls inside
                    canonicalClassName in coroutineCallingClasses
                }
                retransformClasses(classes)
                instrumentedClasses.addAll(classes.map { it.name })
            }

            // In EAGER mode, we re-transform all the classes that were already loaded before the agent installation.
            // New classes will be transformed automatically.
            instrumentationStrategy == InstrumentationStrategy.EAGER -> {
                retransformClasses(getLoadedClassesToInstrument())
            }

            // In a lazy transformation mode, Lincheck processes classes lazily, only when they are used.
            instrumentationStrategy == InstrumentationStrategy.LAZY -> {
                // Clear the set of instrumented classes in case something get wrong during `uninstall`.
                // For instance, it is possible that Lincheck detects a deadlock, `uninstall` is called,
                // but one of the "deadlocked" thread calls `ensureClassHierarchyIsTransformed` after that,
                // adding a new class to `instrumentedClasses`.
                // TODO: distinguish different runs by associating `instrumentedClasses` with `EventTracker`.
                instrumentedClasses.clear()
                // Transform some predefined classes eagerly on start,
                // because often it's the only place when we can do it
                val eagerlyTransformedClasses = getLoadedClassesToInstrument()
                    .filter { isEagerlyInstrumentedClass(it.name) }
                    .toTypedArray()

                retransformClasses(eagerlyTransformedClasses.asList())
                instrumentedClasses.addAll(eagerlyTransformedClasses.map { it.name })
            }
        }
    }