-
Notifications
You must be signed in to change notification settings - Fork 349
/
CodeCoverageStore.java
150 lines (128 loc) · 5.12 KB
/
CodeCoverageStore.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
* Originally based on http://code.google.com/p/javacoveragent/ by
* "alex.mq0" and "dmitry.kandalov" - but don't think anything of the original
* now remains in terms of either code or design.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
// placed in a sun package so non delegating classloaders are likely
// to still delegate it's loading
package sun.pitest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* Store for line visit information.
*/
public final class CodeCoverageStore {
private static final int CLASS_HIT_INDEX = 0;
public static final String CLASS_NAME = CodeCoverageStore.class
.getName()
.replace(
'.',
'/');
private static InvokeReceiver invokeQueue;
private static int classId = 0;
public static final String PROBE_FIELD_NAME = "$$pitCoverageProbes";
public static final String PROBE_LENGTH_FIELD_NAME = "$$pitCoverageProbeSize";
// array of probe hits, first slot indicates any hits to the class.
// testing suggests boolean array with synchronization to ensure happens
// before relationship significantly outperforms
// both AtomicInteger array with bit per flag and integer per flag.
// optimisation with other methods of ensuring a happens before not yet
// investigated
private static final Map<Integer, boolean[]> CLASS_HITS = new ConcurrentHashMap<>();
public static void init(final InvokeReceiver invokeQueue) {
CodeCoverageStore.invokeQueue = invokeQueue;
}
private CodeCoverageStore() {
}
public static synchronized void reset() {
for (final Entry<Integer, boolean[]> each : CLASS_HITS.entrySet()) {
if (each.getValue()[0]) { //Probe 0 gets covered by any method that runs
Arrays.fill(each.getValue(), false);
}
}
}
public static synchronized Collection<Long> getHits() {
final Collection<Long> blockHits = new ArrayList<>();
for (final Entry<Integer, boolean[]> each : CLASS_HITS.entrySet()) {
final boolean[] bs = each.getValue();
// first entry tracks if class has been visited at all
if (!bs[CLASS_HIT_INDEX]) {
continue;
}
final int classId = each.getKey();
for (int probeId = 1; probeId != bs.length; probeId++) {
if (bs[probeId]) {
blockHits.add(encode(classId, probeId));
}
}
}
return blockHits;
}
public static int registerClass(final String className) {
final int id = nextId();
invokeQueue.registerClass(id, className);
return id;
}
public static void registerMethod(final int clazz, final String methodName,
final String methodDesc, final int firstProbe, final int lastProbe) {
invokeQueue.registerProbes(clazz, methodName, methodDesc, firstProbe,
lastProbe);
}
private static synchronized int nextId() {
return classId++;
}
public static int decodeClassId(final long value) {
return (int) (value >> 32);
}
public static int decodeLineId(final long value) {
return (int) value;
}
public static long encode(final int classId, final int line) {
return ((long) classId << 32) | line;
}
public static boolean[] getOrRegisterClassProbes(final int classId,
int probeCount) {
boolean[] ret = CLASS_HITS.putIfAbsent(classId, new boolean[probeCount + 1]);
if (ret == null) {
return CLASS_HITS.get(classId);
}
/*
It's possible that some other java agent has transformed this class, which has
resulted in it getting more blocks. It seems like our intended behavior is to
still collect coverage of these new synthetic blocks, so we need to
make sure that our coverage array grows when the class is re-transformed,
and it's possible that we have already instrumented the class, causing its
coverage array to get set up at the wrong size.
*/
if (ret.length < probeCount + 1) {
synchronized (CLASS_HITS) {
ret = CLASS_HITS.get(classId);
if (ret.length < probeCount + 1) {
ret = new boolean[probeCount + 1];
CLASS_HITS.put(classId, ret);
return ret;
}
}
}
return ret;
}
public static void resetAllStaticState() {
CLASS_HITS.clear();
}
}