1/*
2 * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This code is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * version 2 for more details (a copy is included in the LICENSE file that
12 * accompanied this code).
13 *
14 * You should have received a copy of the GNU General Public License version
15 * 2 along with this work; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 * or visit www.oracle.com if you need additional information or have any
20 * questions.
21 *
22 */
23
24#include "precompiled.hpp"
25#include "code/codeCache.hpp"
26#include "code/nmethod.hpp"
27#include "gc/shenandoah/shenandoahHeap.inline.hpp"
28#include "gc/shenandoah/shenandoahCodeRoots.hpp"
29#include "gc/shenandoah/shenandoahUtils.hpp"
30#include "memory/resourceArea.hpp"
31#include "memory/universe.hpp"
32
33ShenandoahParallelCodeCacheIterator::ShenandoahParallelCodeCacheIterator(const GrowableArray<CodeHeap*>* heaps) {
34 _length = heaps->length();
35 _iters = NEW_C_HEAP_ARRAY(ShenandoahParallelCodeHeapIterator, _length, mtGC);
36 for (int h = 0; h < _length; h++) {
37 _iters[h] = ShenandoahParallelCodeHeapIterator(heaps->at(h));
38 }
39}
40
41ShenandoahParallelCodeCacheIterator::~ShenandoahParallelCodeCacheIterator() {
42 FREE_C_HEAP_ARRAY(ParallelCodeHeapIterator, _iters);
43}
44
45void ShenandoahParallelCodeCacheIterator::parallel_blobs_do(CodeBlobClosure* f) {
46 for (int c = 0; c < _length; c++) {
47 _iters[c].parallel_blobs_do(f);
48 }
49}
50
51ShenandoahParallelCodeHeapIterator::ShenandoahParallelCodeHeapIterator(CodeHeap* heap) :
52 _heap(heap), _claimed_idx(0), _finished(false) {
53}
54
55void ShenandoahParallelCodeHeapIterator::parallel_blobs_do(CodeBlobClosure* f) {
56 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
57
58 /*
59 * Parallel code heap walk.
60 *
61 * This code makes all threads scan all code heaps, but only one thread would execute the
62 * closure on given blob. This is achieved by recording the "claimed" blocks: if a thread
63 * had claimed the block, it can process all blobs in it. Others have to fast-forward to
64 * next attempt without processing.
65 *
66 * Late threads would return immediately if iterator is finished.
67 */
68
69 if (_finished) {
70 return;
71 }
72
73 int stride = 256; // educated guess
74 int stride_mask = stride - 1;
75 assert (is_power_of_2(stride), "sanity");
76
77 int count = 0;
78 bool process_block = true;
79
80 for (CodeBlob *cb = CodeCache::first_blob(_heap); cb != NULL; cb = CodeCache::next_blob(_heap, cb)) {
81 int current = count++;
82 if ((current & stride_mask) == 0) {
83 process_block = (current >= _claimed_idx) &&
84 (Atomic::cmpxchg(current + stride, &_claimed_idx, current) == current);
85 }
86 if (process_block) {
87 if (cb->is_alive()) {
88 f->do_code_blob(cb);
89#ifdef ASSERT
90 if (cb->is_nmethod())
91 Universe::heap()->verify_nmethod((nmethod*)cb);
92#endif
93 }
94 }
95 }
96
97 _finished = true;
98}
99
100class ShenandoahNMethodOopDetector : public OopClosure {
101private:
102 ResourceMark rm; // For growable array allocation below.
103 GrowableArray<oop*> _oops;
104
105public:
106 ShenandoahNMethodOopDetector() : _oops(10) {};
107
108 void do_oop(oop* o) {
109 _oops.append(o);
110 }
111 void do_oop(narrowOop* o) {
112 fatal("NMethods should not have compressed oops embedded.");
113 }
114
115 GrowableArray<oop*>* oops() {
116 return &_oops;
117 }
118
119 bool has_oops() {
120 return !_oops.is_empty();
121 }
122};
123
124GrowableArray<ShenandoahNMethod*>* ShenandoahCodeRoots::_recorded_nms;
125ShenandoahLock ShenandoahCodeRoots::_recorded_nms_lock;
126
127void ShenandoahCodeRoots::initialize() {
128 _recorded_nms = new (ResourceObj::C_HEAP, mtGC) GrowableArray<ShenandoahNMethod*>(100, true, mtGC);
129}
130
131void ShenandoahCodeRoots::add_nmethod(nmethod* nm) {
132 switch (ShenandoahCodeRootsStyle) {
133 case 0:
134 case 1:
135 break;
136 case 2: {
137 assert_locked_or_safepoint(CodeCache_lock);
138 ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock);
139
140 ShenandoahNMethodOopDetector detector;
141 nm->oops_do(&detector);
142
143 if (detector.has_oops()) {
144 ShenandoahNMethod* nmr = new ShenandoahNMethod(nm, detector.oops());
145 nmr->assert_alive_and_correct();
146 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod);
147 if (idx != -1) {
148 ShenandoahNMethod* old = _recorded_nms->at(idx);
149 _recorded_nms->at_put(idx, nmr);
150 delete old;
151 } else {
152 _recorded_nms->append(nmr);
153 }
154 }
155 break;
156 }
157 default:
158 ShouldNotReachHere();
159 }
160};
161
162void ShenandoahCodeRoots::remove_nmethod(nmethod* nm) {
163 switch (ShenandoahCodeRootsStyle) {
164 case 0:
165 case 1: {
166 break;
167 }
168 case 2: {
169 assert_locked_or_safepoint(CodeCache_lock);
170 ShenandoahLocker locker(CodeCache_lock->owned_by_self() ? NULL : &_recorded_nms_lock);
171
172 ShenandoahNMethodOopDetector detector;
173 nm->oops_do(&detector, /* allow_dead = */ true);
174
175 if (detector.has_oops()) {
176 int idx = _recorded_nms->find(nm, ShenandoahNMethod::find_with_nmethod);
177 assert(idx != -1, "nmethod " PTR_FORMAT " should be registered", p2i(nm));
178 ShenandoahNMethod* old = _recorded_nms->at(idx);
179 old->assert_same_oops(detector.oops());
180 _recorded_nms->delete_at(idx);
181 delete old;
182 }
183 break;
184 }
185 default:
186 ShouldNotReachHere();
187 }
188}
189
190ShenandoahCodeRootsIterator::ShenandoahCodeRootsIterator() :
191 _heap(ShenandoahHeap::heap()),
192 _par_iterator(CodeCache::heaps()),
193 _claimed(0) {
194 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
195 assert(!Thread::current()->is_Worker_thread(), "Should not be acquired by workers");
196 switch (ShenandoahCodeRootsStyle) {
197 case 0:
198 case 1: {
199 // No need to do anything here
200 break;
201 }
202 case 2: {
203 CodeCache_lock->lock();
204 break;
205 }
206 default:
207 ShouldNotReachHere();
208 }
209}
210
211ShenandoahCodeRootsIterator::~ShenandoahCodeRootsIterator() {
212 switch (ShenandoahCodeRootsStyle) {
213 case 0:
214 case 1: {
215 // No need to do anything here
216 break;
217 }
218 case 2: {
219 CodeCache_lock->unlock();
220 break;
221 }
222 default:
223 ShouldNotReachHere();
224 }
225}
226
227template<bool CSET_FILTER>
228void ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do(CodeBlobClosure *f) {
229 switch (ShenandoahCodeRootsStyle) {
230 case 0: {
231 if (_seq_claimed.try_set()) {
232 CodeCache::blobs_do(f);
233 }
234 break;
235 }
236 case 1: {
237 _par_iterator.parallel_blobs_do(f);
238 break;
239 }
240 case 2: {
241 ShenandoahCodeRootsIterator::fast_parallel_blobs_do<CSET_FILTER>(f);
242 break;
243 }
244 default:
245 ShouldNotReachHere();
246 }
247}
248
249void ShenandoahAllCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) {
250 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<false>(f);
251}
252
253void ShenandoahCsetCodeRootsIterator::possibly_parallel_blobs_do(CodeBlobClosure *f) {
254 ShenandoahCodeRootsIterator::dispatch_parallel_blobs_do<true>(f);
255}
256
257template <bool CSET_FILTER>
258void ShenandoahCodeRootsIterator::fast_parallel_blobs_do(CodeBlobClosure *f) {
259 assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint");
260
261 size_t stride = 256; // educated guess
262
263 GrowableArray<ShenandoahNMethod*>* list = ShenandoahCodeRoots::_recorded_nms;
264
265 size_t max = (size_t)list->length();
266 while (_claimed < max) {
267 size_t cur = Atomic::add(stride, &_claimed) - stride;
268 size_t start = cur;
269 size_t end = MIN2(cur + stride, max);
270 if (start >= max) break;
271
272 for (size_t idx = start; idx < end; idx++) {
273 ShenandoahNMethod* nmr = list->at((int) idx);
274 nmr->assert_alive_and_correct();
275
276 if (CSET_FILTER && !nmr->has_cset_oops(_heap)) {
277 continue;
278 }
279
280 f->do_code_blob(nmr->nm());
281 }
282 }
283}
284
285ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>* oops) {
286 _nm = nm;
287 _oops = NEW_C_HEAP_ARRAY(oop*, oops->length(), mtGC);
288 _oops_count = oops->length();
289 for (int c = 0; c < _oops_count; c++) {
290 _oops[c] = oops->at(c);
291 }
292}
293
294ShenandoahNMethod::~ShenandoahNMethod() {
295 if (_oops != NULL) {
296 FREE_C_HEAP_ARRAY(oop*, _oops);
297 }
298}
299
300bool ShenandoahNMethod::has_cset_oops(ShenandoahHeap *heap) {
301 for (int c = 0; c < _oops_count; c++) {
302 oop o = RawAccess<>::oop_load(_oops[c]);
303 if (heap->in_collection_set(o)) {
304 return true;
305 }
306 }
307 return false;
308}
309
310#ifdef ASSERT
311void ShenandoahNMethod::assert_alive_and_correct() {
312 assert(_nm->is_alive(), "only alive nmethods here");
313 assert(_oops_count > 0, "should have filtered nmethods without oops before");
314 ShenandoahHeap* heap = ShenandoahHeap::heap();
315 for (int c = 0; c < _oops_count; c++) {
316 oop *loc = _oops[c];
317 assert(_nm->code_contains((address) loc) || _nm->oops_contains(loc), "nmethod should contain the oop*");
318 oop o = RawAccess<>::oop_load(loc);
319 shenandoah_assert_correct_except(loc, o, o == NULL || heap->is_full_gc_move_in_progress());
320 }
321}
322
323void ShenandoahNMethod::assert_same_oops(GrowableArray<oop*>* oops) {
324 assert(_oops_count == oops->length(), "should have the same number of oop*");
325 for (int c = 0; c < _oops_count; c++) {
326 assert(_oops[c] == oops->at(c), "should be the same oop*");
327 }
328}
329#endif
330