1 /*
2 * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "jfr/leakprofiler/chains/edgeStore.hpp"
27 #include "jfr/leakprofiler/chains/edgeUtils.hpp"
28 #include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp"
29 #include "oops/oop.inline.hpp"
30
31 StoredEdge::StoredEdge(const Edge* parent, UnifiedOopRef reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {}
32
33 StoredEdge::StoredEdge(const Edge& edge) : Edge(edge), _gc_root_id(0), _skip_length(0) {}
34
35 StoredEdge::StoredEdge(const StoredEdge& edge) : Edge(edge), _gc_root_id(edge._gc_root_id), _skip_length(edge._skip_length) {}
36
37 traceid EdgeStore::_edge_id_counter = 0;
38
39 EdgeStore::EdgeStore() : _edges(NULL) {
40 _edges = new EdgeHashTable(this);
41 }
42
43 EdgeStore::~EdgeStore() {
44 assert(_edges != NULL, "invariant");
45 delete _edges;
46 }
47
48 bool EdgeStore::is_empty() const {
49 return !_edges->has_entries();
50 }
51
52 void EdgeStore::on_link(EdgeEntry* entry) {
53 assert(entry != NULL, "invariant");
54 assert(entry->id() == 0, "invariant");
55 entry->set_id(++_edge_id_counter);
56 }
57
58 bool EdgeStore::on_equals(uintptr_t hash, const EdgeEntry* entry) {
59 assert(entry != NULL, "invariant");
60 assert(entry->hash() == hash, "invariant");
61 return true;
62 }
63
64 void EdgeStore::on_unlink(EdgeEntry* entry) {
65 assert(entry != NULL, "invariant");
66 // nothing
67 }
207
208 bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t limit) {
209 assert(*previous != NULL, "invariant");
210 assert(*current != NULL, "invariant");
211 size_t depth = 1;
212 while (*current != NULL && depth < limit) {
213 StoredEdge* stored_edge = get((*current)->reference());
214 if (stored_edge != NULL) {
215 link_with_existing_chain(stored_edge, previous, depth);
216 return true;
217 }
218 stored_edge = link_new_edge(previous, current);
219 assert((*previous)->parent() != NULL, "invariant");
220 *previous = stored_edge;
221 *current = (*current)->parent();
222 ++depth;
223 }
224 return NULL == *current;
225 }
226
227 // Install the immediate edge into the mark word of the leak candidate object
228 StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) {
229 assert(edge != NULL, "invariant");
230 assert(!contains(edge->reference()), "invariant");
231 StoredEdge* const leak_context_edge = put(edge->reference());
232 oop sample_object = edge->pointee();
233 assert(sample_object != NULL, "invariant");
234 assert(sample_object->mark().is_marked(), "invariant");
235 sample_object->set_mark(markWord::from_pointer(leak_context_edge));
236 return leak_context_edge;
237 }
238
239 /*
240 * The purpose of put_chain() is to reify the edge sequence
241 * discovered during heap traversal with a normalized logical copy.
242 * This copy consist of two sub-sequences and a connecting link (skip edge).
243 *
244 * "current" can be thought of as the cursor (search) edge, it is not in the edge store.
245 * "previous" is always an edge in the edge store.
246 * The leak context edge is the edge adjacent to the leak candidate object, always an edge in the edge store.
247 */
248 void EdgeStore::put_chain(const Edge* chain, size_t length) {
249 assert(chain != NULL, "invariant");
250 assert(chain->distance_to_root() + 1 == length, "invariant");
251 StoredEdge* const leak_context_edge = associate_leak_context_with_candidate(chain);
252 assert(leak_context_edge != NULL, "invariant");
253 assert(leak_context_edge->parent() == NULL, "invariant");
254
255 if (1 == length) {
|
1 /*
2 * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "jfr/leakprofiler/chains/edgeStore.hpp"
27 #include "jfr/leakprofiler/chains/edgeUtils.hpp"
28 #include "jfr/leakprofiler/sampling/objectSample.hpp"
29 #include "jfr/leakprofiler/utilities/unifiedOopRef.inline.hpp"
30 #include "oops/oop.inline.hpp"
31 #include "runtime/safepoint.hpp"
32
33 StoredEdge::StoredEdge(const Edge* parent, UnifiedOopRef reference) : Edge(parent, reference), _gc_root_id(0), _skip_length(0) {}
34
35 StoredEdge::StoredEdge(const Edge& edge) : Edge(edge), _gc_root_id(0), _skip_length(0) {}
36
37 StoredEdge::StoredEdge(const StoredEdge& edge) : Edge(edge), _gc_root_id(edge._gc_root_id), _skip_length(edge._skip_length) {}
38
39 traceid EdgeStore::_edge_id_counter = 0;
40
41 bool EdgeStore::is_empty() const {
42 return !_edges->has_entries();
43 }
44
45 void EdgeStore::on_link(EdgeEntry* entry) {
46 assert(entry != NULL, "invariant");
47 assert(entry->id() == 0, "invariant");
48 entry->set_id(++_edge_id_counter);
49 }
50
51 bool EdgeStore::on_equals(uintptr_t hash, const EdgeEntry* entry) {
52 assert(entry != NULL, "invariant");
53 assert(entry->hash() == hash, "invariant");
54 return true;
55 }
56
57 void EdgeStore::on_unlink(EdgeEntry* entry) {
58 assert(entry != NULL, "invariant");
59 // nothing
60 }
200
201 bool EdgeStore::put_edges(StoredEdge** previous, const Edge** current, size_t limit) {
202 assert(*previous != NULL, "invariant");
203 assert(*current != NULL, "invariant");
204 size_t depth = 1;
205 while (*current != NULL && depth < limit) {
206 StoredEdge* stored_edge = get((*current)->reference());
207 if (stored_edge != NULL) {
208 link_with_existing_chain(stored_edge, previous, depth);
209 return true;
210 }
211 stored_edge = link_new_edge(previous, current);
212 assert((*previous)->parent() != NULL, "invariant");
213 *previous = stored_edge;
214 *current = (*current)->parent();
215 ++depth;
216 }
217 return NULL == *current;
218 }
219
220 static GrowableArray<const StoredEdge*>* _leak_context_edges = nullptr;
221
222 EdgeStore::EdgeStore() : _edges(new EdgeHashTable(this)) {}
223
224 EdgeStore::~EdgeStore() {
225 assert(_edges != NULL, "invariant");
226 delete _edges;
227 delete _leak_context_edges;
228 _leak_context_edges = nullptr;
229 }
230
231 static int leak_context_edge_idx(const ObjectSample* sample) {
232 assert(sample != nullptr, "invariant");
233 return static_cast<int>(sample->object()->mark().value()) >> markWord::lock_bits;
234 }
235
236 bool EdgeStore::has_leak_context(const ObjectSample* sample) const {
237 const int idx = leak_context_edge_idx(sample);
238 if (idx == 0) {
239 return false;
240 }
241 assert(idx > 0, "invariant");
242 assert(_leak_context_edges != nullptr, "invariant");
243 assert(idx < _leak_context_edges->length(), "invariant");
244 assert(_leak_context_edges->at(idx) != nullptr, "invariant");
245 return true;
246 }
247
248 const StoredEdge* EdgeStore::get(const ObjectSample* sample) const {
249 assert(sample != nullptr, "invariant");
250 if (_leak_context_edges != nullptr) {
251 assert(SafepointSynchronize::is_at_safepoint(), "invariant");
252 const int idx = leak_context_edge_idx(sample);
253 if (idx > 0) {
254 assert(idx < _leak_context_edges->length(), "invariant");
255 const StoredEdge* const edge =_leak_context_edges->at(idx);
256 assert(edge != nullptr, "invariant");
257 return edge;
258 }
259 }
260 return get(UnifiedOopRef::encode_in_native(sample->object_addr()));
261 }
262
263 #ifdef ASSERT
264 // max_idx to ensure idx fit in lower 32-bits of markword together with lock bits.
265 static constexpr const int max_idx = right_n_bits(32 - markWord::lock_bits);
266
267 static void store_idx_precondition(oop sample_object, int idx) {
268 assert(sample_object != NULL, "invariant");
269 assert(sample_object->mark().is_marked(), "invariant");
270 assert(idx > 0, "invariant");
271 assert(idx <= max_idx, "invariant");
272 }
273 #endif
274
275 static void store_idx_in_markword(oop sample_object, int idx) {
276 DEBUG_ONLY(store_idx_precondition(sample_object, idx);)
277 const markWord idx_mark_word(sample_object->mark().value() | idx << markWord::lock_bits);
278 sample_object->set_mark(idx_mark_word);
279 assert(sample_object->mark().is_marked(), "must still be marked");
280 }
281
282 static const int initial_size = 64;
283
284 static int save(const StoredEdge* edge) {
285 assert(edge != nullptr, "invariant");
286 if (_leak_context_edges == nullptr) {
287 _leak_context_edges = new (ResourceObj::C_HEAP, mtTracing)GrowableArray<const StoredEdge*>(initial_size, mtTracing);
288 _leak_context_edges->append(nullptr); // next idx now at 1, for disambiguation in markword.
289 }
290 return _leak_context_edges->append(edge);
291 }
292
293 // We associate the leak context edge with the leak candidate object by saving the
294 // edge in an array and storing the array idx (shifted) into the markword of the candidate object.
295 static void associate_with_candidate(const StoredEdge* leak_context_edge) {
296 assert(leak_context_edge != nullptr, "invariant");
297 store_idx_in_markword(leak_context_edge->pointee(), save(leak_context_edge));
298 }
299
300 StoredEdge* EdgeStore::associate_leak_context_with_candidate(const Edge* edge) {
301 assert(edge != NULL, "invariant");
302 assert(!contains(edge->reference()), "invariant");
303 StoredEdge* const leak_context_edge = put(edge->reference());
304 associate_with_candidate(leak_context_edge);
305 return leak_context_edge;
306 }
307
308 /*
309 * The purpose of put_chain() is to reify the edge sequence
310 * discovered during heap traversal with a normalized logical copy.
311 * This copy consist of two sub-sequences and a connecting link (skip edge).
312 *
313 * "current" can be thought of as the cursor (search) edge, it is not in the edge store.
314 * "previous" is always an edge in the edge store.
315 * The leak context edge is the edge adjacent to the leak candidate object, always an edge in the edge store.
316 */
317 void EdgeStore::put_chain(const Edge* chain, size_t length) {
318 assert(chain != NULL, "invariant");
319 assert(chain->distance_to_root() + 1 == length, "invariant");
320 StoredEdge* const leak_context_edge = associate_leak_context_with_candidate(chain);
321 assert(leak_context_edge != NULL, "invariant");
322 assert(leak_context_edge->parent() == NULL, "invariant");
323
324 if (1 == length) {
|