1/*
2 * Copyright (c) 2014, 2019, 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/jfrEvents.hpp"
27#include "jfr/recorder/jfrRecorder.hpp"
28#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"
29#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
30#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"
31#include "jfr/leakprofiler/chains/edgeStore.hpp"
32#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"
33#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"
34#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp"
35#include "jfr/leakprofiler/leakProfiler.hpp"
36#include "jfr/leakprofiler/sampling/objectSample.hpp"
37#include "jfr/leakprofiler/sampling/objectSampler.hpp"
38#include "jfr/leakprofiler/utilities/rootType.hpp"
39#include "jfr/metadata/jfrSerializer.hpp"
40#include "runtime/interfaceSupport.inline.hpp"
41#include "runtime/mutexLocker.hpp"
42#include "runtime/thread.inline.hpp"
43
44template <typename SampleProcessor>
45static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) {
46 assert(sample != NULL, "invariant");
47 while (sample != end) {
48 processor.sample_do(sample);
49 sample = sample->next();
50 }
51}
52
53class RootSystemType : public JfrSerializer {
54 public:
55 void serialize(JfrCheckpointWriter& writer) {
56 const u4 nof_root_systems = OldObjectRoot::_number_of_systems;
57 writer.write_count(nof_root_systems);
58 for (u4 i = 0; i < nof_root_systems; ++i) {
59 writer.write_key(i);
60 writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i));
61 }
62 }
63};
64
65class RootType : public JfrSerializer {
66 public:
67 void serialize(JfrCheckpointWriter& writer) {
68 const u4 nof_root_types = OldObjectRoot::_number_of_types;
69 writer.write_count(nof_root_types);
70 for (u4 i = 0; i < nof_root_types; ++i) {
71 writer.write_key(i);
72 writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i));
73 }
74 }
75};
76
77class CheckpointInstall {
78 private:
79 const JfrCheckpointBlobHandle& _cp;
80 public:
81 CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {}
82 void sample_do(ObjectSample* sample) {
83 assert(sample != NULL, "invariant");
84 if (!sample->is_dead()) {
85 sample->set_klass_checkpoint(_cp);
86 }
87 }
88};
89
90class CheckpointWrite {
91 private:
92 JfrCheckpointWriter& _writer;
93 const jlong _last_sweep;
94 public:
95 CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {}
96 void sample_do(ObjectSample* sample) {
97 assert(sample != NULL, "invariant");
98 if (sample->is_alive_and_older_than(_last_sweep)) {
99 if (sample->has_thread_checkpoint()) {
100 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
101 thread_cp->exclusive_write(_writer);
102 }
103 if (sample->has_klass_checkpoint()) {
104 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
105 klass_cp->exclusive_write(_writer);
106 }
107 }
108 }
109};
110
111class CheckpointStateReset {
112 private:
113 const jlong _last_sweep;
114 public:
115 CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {}
116 void sample_do(ObjectSample* sample) {
117 assert(sample != NULL, "invariant");
118 if (sample->is_alive_and_older_than(_last_sweep)) {
119 if (sample->has_thread_checkpoint()) {
120 const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint();
121 thread_cp->reset_write_state();
122 }
123 if (sample->has_klass_checkpoint()) {
124 const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint();
125 klass_cp->reset_write_state();
126 }
127 }
128 }
129};
130
131class StackTraceWrite {
132 private:
133 JfrStackTraceRepository& _stack_trace_repo;
134 JfrCheckpointWriter& _writer;
135 int _count;
136 public:
137 StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) :
138 _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) {
139 JfrStacktrace_lock->lock_without_safepoint_check();
140 }
141 ~StackTraceWrite() {
142 assert(JfrStacktrace_lock->owned_by_self(), "invariant");
143 JfrStacktrace_lock->unlock();
144 }
145
146 void sample_do(ObjectSample* sample) {
147 assert(sample != NULL, "invariant");
148 if (!sample->is_dead()) {
149 if (sample->has_stack_trace()) {
150 JfrTraceId::use(sample->klass(), true);
151 _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash());
152 ++_count;
153 }
154 }
155 }
156
157 int count() const {
158 return _count;
159 }
160};
161
162class SampleMark {
163 private:
164 ObjectSampleMarker& _marker;
165 jlong _last_sweep;
166 int _count;
167 public:
168 SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker),
169 _last_sweep(last_sweep),
170 _count(0) {}
171 void sample_do(ObjectSample* sample) {
172 assert(sample != NULL, "invariant");
173 if (sample->is_alive_and_older_than(_last_sweep)) {
174 _marker.mark(sample->object());
175 ++_count;
176 }
177 }
178
179 int count() const {
180 return _count;
181 }
182};
183
184void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload) {
185 if (!writer.has_data()) {
186 return;
187 }
188
189 assert(writer.has_data(), "invariant");
190 const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob();
191
192 // Class unload implies a safepoint.
193 // Not class unload implies the object sampler is locked, because it was claimed exclusively earlier.
194 // Therefore: direct access the object sampler instance is safe.
195 const ObjectSampler* const object_sampler = ObjectSampler::sampler();
196 assert(object_sampler != NULL, "invariant");
197
198 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
199 const ObjectSample* const last_resolved = object_sampler->last_resolved();
200 CheckpointInstall install(h_cp);
201
202 if (class_unload) {
203 // all samples need class unload information
204 do_samples(last, NULL, install);
205 return;
206 }
207
208 // only new samples since last resolved checkpoint
209 if (last != last_resolved) {
210 do_samples(last, last_resolved, install);
211 const_cast<ObjectSampler*>(object_sampler)->set_last_resolved(last);
212 }
213}
214
215void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {
216 assert(sampler != NULL, "invariant");
217 assert(edge_store != NULL, "invariant");
218 assert(thread != NULL, "invariant");
219
220 static bool types_registered = false;
221 if (!types_registered) {
222 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType());
223 JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType());
224 types_registered = true;
225 }
226
227 const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value();
228 ObjectSample* const last = const_cast<ObjectSample*>(sampler->last());
229 {
230 JfrCheckpointWriter writer(false, false, thread);
231 CheckpointWrite checkpoint_write(writer, last_sweep);
232 do_samples(last, NULL, checkpoint_write);
233 }
234
235 CheckpointStateReset state_reset(last_sweep);
236 do_samples(last, NULL, state_reset);
237
238 if (!edge_store->is_empty()) {
239 // java object and chain representations
240 JfrCheckpointWriter writer(false, true, thread);
241 ObjectSampleWriter osw(writer, edge_store);
242 edge_store->iterate(osw);
243 }
244}
245
246int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) {
247 assert(object_sampler != NULL, "invariant");
248 ObjectSample* const last = const_cast<ObjectSample*>(object_sampler->last());
249 if (last == NULL) {
250 return 0;
251 }
252 const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value();
253 SampleMark mark(marker, last_sweep);
254 do_samples(last, NULL, mark);
255 return mark.count();
256}
257
258WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) :
259 _sampler(sampler), _stack_trace_repo(repo) {}
260
261bool WriteObjectSampleStacktrace::process() {
262 assert(LeakProfiler::is_running(), "invariant");
263 assert(_sampler != NULL, "invariant");
264
265 ObjectSample* const last = const_cast<ObjectSample*>(_sampler->last());
266 const ObjectSample* const last_resolved = _sampler->last_resolved();
267 if (last == last_resolved) {
268 return true;
269 }
270
271 JfrCheckpointWriter writer(false, true, Thread::current());
272 const JfrCheckpointContext ctx = writer.context();
273
274 writer.write_type(TYPE_STACKTRACE);
275 const jlong count_offset = writer.reserve(sizeof(u4));
276
277 int count = 0;
278 {
279 StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock
280 do_samples(last, last_resolved, stack_trace_write);
281 count = stack_trace_write.count();
282 }
283 if (count == 0) {
284 writer.set_context(ctx);
285 return true;
286 }
287 assert(count > 0, "invariant");
288 writer.write_count((u4)count, count_offset);
289 JfrStackTraceRepository::write_metadata(writer);
290
291 // install the stacktrace checkpoint information to the candidates
292 ObjectSampleCheckpoint::install(writer, false);
293 return true;
294}
295