-
Notifications
You must be signed in to change notification settings - Fork 34
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
Static class initialization code is not guaranteed to run in ignored section #419
Comments
There are several possible ways to resolve this issue. (1) Hard-coded solution for #376We can handle the specific execution pattern occured in #376 involving (2) Instrument
|
CC @ndkoval |
#423 implements temporary "quickfix" solution (1) for this problem. |
As for solution (3), after another round of discussion with @ndkoval we decided that we cannot completely forbid analysis of |
As such, solution (4) also cannot be applied in its current form. |
Therefore, we can propose solution (5). (5) Wrap calls of non-instrumented methods into ignored sectionsWe can wrap calls of non-instrumented methods, called from instrumented methods, into ignored sections. This approach ensures that they will be not analyzed, even if they itself call some methods from instrumented classes. Additionally, as an optimization, we can wrap only non-instrumented methods that may call instrumented methods. This approach would still require some form of call-graph analysis. |
From what I understand from the Lincheck codebase, we have an intent to maintain the invariant that static class initialization blocks
clinit
are always executed in ignored sections:lincheck/src/jvm/main/org/jetbrains/kotlinx/lincheck/transformation/LincheckClassVisitor.kt
Line 92 in 800f590
However, it looks like that currently this invariant can be broken due to a complicated interaction of instrumented and non-instrumented code.
Consider the following example:
This is what will happen when the test will be executed:
C
will be created and its methodfoo()
will be called.foo()
, class loading ofB
will be requested, and its<clinit>
block will be executed.B
itself is not instrumented, its<clinit>
block will not be wrapped into ignored section by the Lincheck instrumentation.B::<clinit>
will callA::foo()
, which is instrumented, and because the code is not in ignored section, it will be tracked by Lincheck.This situation lead to various problems:
<clinit>
block will not be executed on replay, leading to non-determinism error.<clinit>
block may throw exceptions (e.g. "spin-loop replay request" exception), leading to errors in class loading proccess, such asExceptionInInitializerError
orNoClassDefFoundError
.In the particular case of #376 we had
NoClassDefFoundError
, occured because of earlierExceptionInInitializerError
thrown from<clinit>
. In the case of that issue we had the following concrete classes from the example above:class C
-- some instrumented class from the tested data structure itself,class B := java.lang.StackTraceElement
-- not instrumented by Lincheck according to these rules.class A := java.util.HashSet
-- instrumented by Lincheck according to these rules.To see where
HashSet
is used inStackTraceElement
look at the source code.The text was updated successfully, but these errors were encountered: