Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/jfr/leakprofiler/chains/edgeStore.cpp
41153 views
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
}
68
69
#ifdef ASSERT
70
bool EdgeStore::contains(UnifiedOopRef reference) const {
71
return get(reference) != NULL;
72
}
73
#endif
74
75
StoredEdge* EdgeStore::get(UnifiedOopRef reference) const {
76
assert(!reference.is_null(), "invariant");
77
EdgeEntry* const entry = _edges->lookup_only(reference.addr<uintptr_t>());
78
return entry != NULL ? entry->literal_addr() : NULL;
79
}
80
81
StoredEdge* EdgeStore::put(UnifiedOopRef reference) {
82
assert(!reference.is_null(), "invariant");
83
const StoredEdge e(NULL, reference);
84
assert(NULL == _edges->lookup_only(reference.addr<uintptr_t>()), "invariant");
85
EdgeEntry& entry = _edges->put(reference.addr<uintptr_t>(), e);
86
return entry.literal_addr();
87
}
88
89
traceid EdgeStore::get_id(const Edge* edge) const {
90
assert(edge != NULL, "invariant");
91
EdgeEntry* const entry = _edges->lookup_only(edge->reference().addr<uintptr_t>());
92
assert(entry != NULL, "invariant");
93
return entry->id();
94
}
95
96
traceid EdgeStore::gc_root_id(const Edge* edge) const {
97
assert(edge != NULL, "invariant");
98
const traceid gc_root_id = static_cast<const StoredEdge*>(edge)->gc_root_id();
99
if (gc_root_id != 0) {
100
return gc_root_id;
101
}
102
// not cached
103
assert(edge != NULL, "invariant");
104
const Edge* const root = EdgeUtils::root(*edge);
105
assert(root != NULL, "invariant");
106
assert(root->parent() == NULL, "invariant");
107
return get_id(root);
108
}
109
110
static const Edge* get_skip_ancestor(const Edge** current, size_t distance_to_root, size_t* skip_length) {
111
assert(distance_to_root >= EdgeUtils::root_context, "invariant");
112
assert(*skip_length == 0, "invariant");
113
*skip_length = distance_to_root - (EdgeUtils::root_context - 1);
114
const Edge* const target = EdgeUtils::ancestor(**current, *skip_length);
115
assert(target != NULL, "invariant");
116
assert(target->distance_to_root() + 1 == EdgeUtils::root_context, "invariant");
117
return target;
118
}
119
120
bool EdgeStore::put_skip_edge(StoredEdge** previous, const Edge** current, size_t distance_to_root) {
121
assert(*previous != NULL, "invariant");
122
assert((*previous)->parent() == NULL, "invariant");
123
assert(*current != NULL, "invariant");
124
assert((*current)->distance_to_root() == distance_to_root, "invariant");
125
126
if (distance_to_root < EdgeUtils::root_context) {
127
// nothing to skip
128
return false;
129
}
130
131
size_t skip_length = 0;
132
const Edge* const skip_ancestor = get_skip_ancestor(current, distance_to_root, &skip_length);
133
assert(skip_ancestor != NULL, "invariant");
134
(*previous)->set_skip_length(skip_length);
135
136
// lookup target
137
StoredEdge* stored_target = get(skip_ancestor->reference());
138
if (stored_target != NULL) {
139
(*previous)->set_parent(stored_target);
140
// linked to existing, complete
141
return true;
142
}
143
144
assert(stored_target == NULL, "invariant");
145
stored_target = put(skip_ancestor->reference());
146
assert(stored_target != NULL, "invariant");
147
(*previous)->set_parent(stored_target);
148
*previous = stored_target;
149
*current = skip_ancestor->parent();
150
return false;
151
}
152
153
static void link_edge(const StoredEdge* current_stored, StoredEdge** previous) {
154
assert(current_stored != NULL, "invariant");
155
assert(*previous != NULL, "invariant");
156
assert((*previous)->parent() == NULL, "invariant");
157
(*previous)->set_parent(current_stored);
158
}
159
160
static const StoredEdge* find_closest_skip_edge(const StoredEdge* edge, size_t* distance) {
161
assert(edge != NULL, "invariant");
162
assert(distance != NULL, "invariant");
163
const StoredEdge* current = edge;
164
*distance = 1;
165
while (current != NULL && !current->is_skip_edge()) {
166
++(*distance);
167
current = current->parent();
168
}
169
return current;
170
}
171
172
void EdgeStore::link_with_existing_chain(const StoredEdge* current_stored, StoredEdge** previous, size_t previous_length) {
173
assert(current_stored != NULL, "invariant");
174
assert((*previous)->parent() == NULL, "invariant");
175
size_t distance_to_skip_edge; // including the skip edge itself
176
const StoredEdge* const closest_skip_edge = find_closest_skip_edge(current_stored, &distance_to_skip_edge);
177
if (closest_skip_edge == NULL) {
178
// no found skip edge implies root
179
if (distance_to_skip_edge + previous_length <= EdgeUtils::max_ref_chain_depth) {
180
link_edge(current_stored, previous);
181
return;
182
}
183
assert(current_stored->distance_to_root() == distance_to_skip_edge - 2, "invariant");
184
put_skip_edge(previous, reinterpret_cast<const Edge**>(&current_stored), distance_to_skip_edge - 2);
185
return;
186
}
187
assert(closest_skip_edge->is_skip_edge(), "invariant");
188
if (distance_to_skip_edge + previous_length <= EdgeUtils::leak_context) {
189
link_edge(current_stored, previous);
190
return;
191
}
192
// create a new skip edge with derived information from closest skip edge
193
(*previous)->set_skip_length(distance_to_skip_edge + closest_skip_edge->skip_length());
194
(*previous)->set_parent(closest_skip_edge->parent());
195
}
196
197
StoredEdge* EdgeStore::link_new_edge(StoredEdge** previous, const Edge** current) {
198
assert(*previous != NULL, "invariant");
199
assert((*previous)->parent() == NULL, "invariant");
200
assert(*current != NULL, "invariant");
201
assert(!contains((*current)->reference()), "invariant");
202
StoredEdge* const stored_edge = put((*current)->reference());
203
assert(stored_edge != NULL, "invariant");
204
link_edge(stored_edge, previous);
205
return stored_edge;
206
}
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) {
256
store_gc_root_id_in_leak_context_edge(leak_context_edge, leak_context_edge);
257
return;
258
}
259
260
const Edge* current = chain->parent();
261
assert(current != NULL, "invariant");
262
StoredEdge* previous = leak_context_edge;
263
264
// a leak context is the sequence of (limited) edges reachable from the leak candidate
265
if (put_edges(&previous, &current, EdgeUtils::leak_context)) {
266
// complete
267
assert(previous != NULL, "invariant");
268
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous));
269
return;
270
}
271
272
const size_t distance_to_root = length > EdgeUtils::leak_context ? length - 1 - EdgeUtils::leak_context : length - 1;
273
assert(current->distance_to_root() == distance_to_root, "invariant");
274
275
// a skip edge is the logical link
276
// connecting the leak context sequence with the root context sequence
277
if (put_skip_edge(&previous, &current, distance_to_root)) {
278
// complete
279
assert(previous != NULL, "invariant");
280
assert(previous->is_skip_edge(), "invariant");
281
assert(previous->parent() != NULL, "invariant");
282
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous->parent()));
283
return;
284
}
285
286
assert(current->distance_to_root() < EdgeUtils::root_context, "invariant");
287
288
// a root context is the sequence of (limited) edges reachable from the root
289
put_edges(&previous, &current, EdgeUtils::root_context);
290
assert(previous != NULL, "invariant");
291
put_chain_epilogue(leak_context_edge, EdgeUtils::root(*previous));
292
}
293
294
void EdgeStore::put_chain_epilogue(StoredEdge* leak_context_edge, const Edge* root) const {
295
assert(leak_context_edge != NULL, "invariant");
296
assert(root != NULL, "invariant");
297
store_gc_root_id_in_leak_context_edge(leak_context_edge, root);
298
assert(leak_context_edge->distance_to_root() + 1 <= EdgeUtils::max_ref_chain_depth, "invariant");
299
}
300
301
// To avoid another traversal to resolve the root edge id later,
302
// cache it in the immediate leak context edge for fast retrieval.
303
void EdgeStore::store_gc_root_id_in_leak_context_edge(StoredEdge* leak_context_edge, const Edge* root) const {
304
assert(leak_context_edge != NULL, "invariant");
305
assert(leak_context_edge->gc_root_id() == 0, "invariant");
306
assert(root != NULL, "invariant");
307
assert(root->parent() == NULL, "invariant");
308
assert(root->distance_to_root() == 0, "invariant");
309
const StoredEdge* const stored_root = static_cast<const StoredEdge*>(root);
310
traceid root_id = stored_root->gc_root_id();
311
if (root_id == 0) {
312
root_id = get_id(root);
313
stored_root->set_gc_root_id(root_id);
314
}
315
assert(root_id != 0, "invariant");
316
leak_context_edge->set_gc_root_id(root_id);
317
assert(leak_context_edge->gc_root_id() == stored_root->gc_root_id(), "invariant");
318
}
319
320