|
1 /* |
|
2 * Copyright (c) 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/leakprofiler/chains/edgeStore.hpp" |
|
28 #include "jfr/leakprofiler/chains/pathToGcRootsOperation.hpp" |
|
29 #include "jfr/leakprofiler/checkpoint/eventEmitter.hpp" |
|
30 #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" |
|
31 #include "jfr/leakprofiler/sampling/objectSample.hpp" |
|
32 #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
|
33 #include "logging/log.hpp" |
|
34 #include "memory/resourceArea.hpp" |
|
35 #include "oops/markOop.hpp" |
|
36 #include "oops/oop.inline.hpp" |
|
37 #include "runtime/thread.inline.hpp" |
|
38 #include "runtime/vmThread.hpp" |
|
39 |
|
40 EventEmitter::EventEmitter(const JfrTicks& start_time, const JfrTicks& end_time) : |
|
41 _start_time(start_time), |
|
42 _end_time(end_time), |
|
43 _thread(Thread::current()), |
|
44 _jfr_thread_local(_thread->jfr_thread_local()), |
|
45 _thread_id(_thread->jfr_thread_local()->thread_id()) {} |
|
46 |
|
47 EventEmitter::~EventEmitter() { |
|
48 // restore / reset thread local stack trace and thread id |
|
49 _jfr_thread_local->set_thread_id(_thread_id); |
|
50 _jfr_thread_local->clear_cached_stack_trace(); |
|
51 } |
|
52 |
|
53 void EventEmitter::emit(ObjectSampler* sampler, int64_t cutoff_ticks, bool emit_all) { |
|
54 assert(sampler != NULL, "invariant"); |
|
55 |
|
56 ResourceMark rm; |
|
57 EdgeStore edge_store; |
|
58 if (cutoff_ticks <= 0) { |
|
59 // no reference chains |
|
60 JfrTicks time_stamp = JfrTicks::now(); |
|
61 EventEmitter emitter(time_stamp, time_stamp); |
|
62 emitter.write_events(sampler, &edge_store, emit_all); |
|
63 return; |
|
64 } |
|
65 // events emitted with reference chains require a safepoint operation |
|
66 PathToGcRootsOperation op(sampler, &edge_store, cutoff_ticks, emit_all); |
|
67 VMThread::execute(&op); |
|
68 } |
|
69 |
|
70 size_t EventEmitter::write_events(ObjectSampler* object_sampler, EdgeStore* edge_store, bool emit_all) { |
|
71 assert(_thread == Thread::current(), "invariant"); |
|
72 assert(_thread->jfr_thread_local() == _jfr_thread_local, "invariant"); |
|
73 assert(object_sampler != NULL, "invariant"); |
|
74 assert(edge_store != NULL, "invariant"); |
|
75 |
|
76 const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); |
|
77 size_t count = 0; |
|
78 |
|
79 const ObjectSample* current = object_sampler->first(); |
|
80 while (current != NULL) { |
|
81 ObjectSample* prev = current->prev(); |
|
82 if (current->is_alive_and_older_than(last_sweep)) { |
|
83 write_event(current, edge_store); |
|
84 ++count; |
|
85 } |
|
86 current = prev; |
|
87 } |
|
88 |
|
89 if (count > 0) { |
|
90 // serialize associated checkpoints and potential chains |
|
91 ObjectSampleCheckpoint::write(object_sampler, edge_store, emit_all, _thread); |
|
92 } |
|
93 return count; |
|
94 } |
|
95 |
|
96 static int array_size(const oop object) { |
|
97 assert(object != NULL, "invariant"); |
|
98 if (object->is_array()) { |
|
99 return arrayOop(object)->length(); |
|
100 } |
|
101 return min_jint; |
|
102 } |
|
103 |
|
104 void EventEmitter::write_event(const ObjectSample* sample, EdgeStore* edge_store) { |
|
105 assert(sample != NULL, "invariant"); |
|
106 assert(!sample->is_dead(), "invariant"); |
|
107 assert(edge_store != NULL, "invariant"); |
|
108 assert(_jfr_thread_local != NULL, "invariant"); |
|
109 |
|
110 const oop* object_addr = sample->object_addr(); |
|
111 traceid gc_root_id = 0; |
|
112 const Edge* edge = NULL; |
|
113 if (SafepointSynchronize::is_at_safepoint()) { |
|
114 edge = (const Edge*)(*object_addr)->mark(); |
|
115 } |
|
116 if (edge == NULL) { |
|
117 // In order to dump out a representation of the event |
|
118 // even though it was not reachable / too long to reach, |
|
119 // we need to register a top level edge for this object. |
|
120 edge = edge_store->put(object_addr); |
|
121 } else { |
|
122 gc_root_id = edge_store->gc_root_id(edge); |
|
123 } |
|
124 |
|
125 assert(edge != NULL, "invariant"); |
|
126 const traceid object_id = edge_store->get_id(edge); |
|
127 assert(object_id != 0, "invariant"); |
|
128 |
|
129 EventOldObjectSample e(UNTIMED); |
|
130 e.set_starttime(_start_time); |
|
131 e.set_endtime(_end_time); |
|
132 e.set_allocationTime(sample->allocation_time()); |
|
133 e.set_lastKnownHeapUsage(sample->heap_used_at_last_gc()); |
|
134 e.set_object(object_id); |
|
135 e.set_arrayElements(array_size(edge->pointee())); |
|
136 e.set_root(gc_root_id); |
|
137 |
|
138 // Temporarily assigning both the stack trace id and thread id |
|
139 // onto the thread local data structure of the emitter thread (for the duration |
|
140 // of the commit() call). This trick provides a means to override |
|
141 // the event generation mechanism by injecting externally provided id's. |
|
142 // At this particular location, it allows us to emit an old object event |
|
143 // supplying information from where the actual sampling occurred. |
|
144 _jfr_thread_local->set_cached_stack_trace_id(sample->stack_trace_id()); |
|
145 assert(sample->has_thread(), "invariant"); |
|
146 _jfr_thread_local->set_thread_id(sample->thread_id()); |
|
147 e.commit(); |
|
148 } |