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 | |
26 | #include "jfr/jfrEvents.hpp" |
27 | #include "gc/shared/gcCause.hpp" |
28 | #include "gc/shared/gcTimer.hpp" |
29 | #include "gc/shared/gcTrace.hpp" |
30 | #include "gc/shared/gcWhen.hpp" |
31 | #include "gc/shenandoah/shenandoahAllocTracker.hpp" |
32 | #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" |
33 | #include "gc/shenandoah/shenandoahMarkCompact.hpp" |
34 | #include "gc/shenandoah/shenandoahHeap.hpp" |
35 | #include "gc/shenandoah/shenandoahHeuristics.hpp" |
36 | #include "gc/shenandoah/shenandoahUtils.hpp" |
37 | |
38 | ShenandoahPhaseTimings::Phase ShenandoahGCPhase::_current_phase = ShenandoahGCPhase::_invalid_phase; |
39 | |
40 | ShenandoahGCSession::ShenandoahGCSession(GCCause::Cause cause) : |
41 | _heap(ShenandoahHeap::heap()), |
42 | _timer(_heap->gc_timer()), |
43 | _tracer(_heap->tracer()) { |
44 | assert(!ShenandoahGCPhase::is_valid_phase(ShenandoahGCPhase::current_phase()), |
45 | "No current GC phase" ); |
46 | |
47 | _heap->set_gc_cause(cause); |
48 | _timer->register_gc_start(); |
49 | _tracer->report_gc_start(cause, _timer->gc_start()); |
50 | _heap->trace_heap(GCWhen::BeforeGC, _tracer); |
51 | |
52 | _heap->shenandoah_policy()->record_cycle_start(); |
53 | _heap->heuristics()->record_cycle_start(); |
54 | _trace_cycle.initialize(_heap->cycle_memory_manager(), cause, |
55 | /* allMemoryPoolsAffected */ true, |
56 | /* recordGCBeginTime = */ true, |
57 | /* recordPreGCUsage = */ true, |
58 | /* recordPeakUsage = */ true, |
59 | /* recordPostGCUsage = */ true, |
60 | /* recordAccumulatedGCTime = */ true, |
61 | /* recordGCEndTime = */ true, |
62 | /* countCollection = */ true |
63 | ); |
64 | } |
65 | |
66 | ShenandoahGCSession::~ShenandoahGCSession() { |
67 | _heap->heuristics()->record_cycle_end(); |
68 | _timer->register_gc_end(); |
69 | _heap->trace_heap(GCWhen::AfterGC, _tracer); |
70 | _tracer->report_gc_end(_timer->gc_end(), _timer->time_partitions()); |
71 | assert(!ShenandoahGCPhase::is_valid_phase(ShenandoahGCPhase::current_phase()), |
72 | "No current GC phase" ); |
73 | _heap->set_gc_cause(GCCause::_no_gc); |
74 | } |
75 | |
76 | ShenandoahGCPauseMark::ShenandoahGCPauseMark(uint gc_id, SvcGCMarker::reason_type type) : |
77 | _heap(ShenandoahHeap::heap()), _gc_id_mark(gc_id), _svc_gc_mark(type), _is_gc_active_mark() { |
78 | |
79 | // FIXME: It seems that JMC throws away level 0 events, which are the Shenandoah |
80 | // pause events. Create this pseudo level 0 event to push real events to level 1. |
81 | _heap->gc_timer()->register_gc_phase_start("Shenandoah" , Ticks::now()); |
82 | _trace_pause.initialize(_heap->stw_memory_manager(), _heap->gc_cause(), |
83 | /* allMemoryPoolsAffected */ true, |
84 | /* recordGCBeginTime = */ true, |
85 | /* recordPreGCUsage = */ false, |
86 | /* recordPeakUsage = */ false, |
87 | /* recordPostGCUsage = */ false, |
88 | /* recordAccumulatedGCTime = */ true, |
89 | /* recordGCEndTime = */ true, |
90 | /* countCollection = */ true |
91 | ); |
92 | |
93 | _heap->heuristics()->record_gc_start(); |
94 | } |
95 | |
96 | ShenandoahGCPauseMark::~ShenandoahGCPauseMark() { |
97 | _heap->gc_timer()->register_gc_phase_end(Ticks::now()); |
98 | _heap->heuristics()->record_gc_end(); |
99 | } |
100 | |
101 | ShenandoahGCPhase::ShenandoahGCPhase(const ShenandoahPhaseTimings::Phase phase) : |
102 | _heap(ShenandoahHeap::heap()), _phase(phase) { |
103 | assert(!Thread::current()->is_Worker_thread() && |
104 | (Thread::current()->is_VM_thread() || |
105 | Thread::current()->is_ConcurrentGC_thread()), |
106 | "Must be set by these threads" ); |
107 | _parent_phase = _current_phase; |
108 | _current_phase = phase; |
109 | |
110 | _heap->phase_timings()->record_phase_start(_phase); |
111 | } |
112 | |
113 | ShenandoahGCPhase::~ShenandoahGCPhase() { |
114 | _heap->phase_timings()->record_phase_end(_phase); |
115 | _current_phase = _parent_phase; |
116 | } |
117 | |
118 | bool ShenandoahGCPhase::is_valid_phase(ShenandoahPhaseTimings::Phase phase) { |
119 | return phase >= 0 && phase < ShenandoahPhaseTimings::_num_phases; |
120 | } |
121 | |
122 | bool ShenandoahGCPhase::is_root_work_phase() { |
123 | switch(current_phase()) { |
124 | case ShenandoahPhaseTimings::scan_roots: |
125 | case ShenandoahPhaseTimings::update_roots: |
126 | case ShenandoahPhaseTimings::init_evac: |
127 | case ShenandoahPhaseTimings::final_update_refs_roots: |
128 | case ShenandoahPhaseTimings::degen_gc_update_roots: |
129 | case ShenandoahPhaseTimings::init_traversal_gc_work: |
130 | case ShenandoahPhaseTimings::final_traversal_gc_work: |
131 | case ShenandoahPhaseTimings::final_traversal_update_roots: |
132 | case ShenandoahPhaseTimings::full_gc_roots: |
133 | return true; |
134 | default: |
135 | return false; |
136 | } |
137 | } |
138 | |
139 | ShenandoahAllocTrace::ShenandoahAllocTrace(size_t words_size, ShenandoahAllocRequest::Type alloc_type) { |
140 | if (ShenandoahAllocationTrace) { |
141 | _start = os::elapsedTime(); |
142 | _size = words_size; |
143 | _alloc_type = alloc_type; |
144 | } else { |
145 | _start = 0; |
146 | _size = 0; |
147 | _alloc_type = ShenandoahAllocRequest::Type(0); |
148 | } |
149 | } |
150 | |
151 | ShenandoahAllocTrace::~ShenandoahAllocTrace() { |
152 | if (ShenandoahAllocationTrace) { |
153 | double stop = os::elapsedTime(); |
154 | double duration_sec = stop - _start; |
155 | double duration_us = duration_sec * 1000000; |
156 | ShenandoahAllocTracker* tracker = ShenandoahHeap::heap()->alloc_tracker(); |
157 | assert(tracker != NULL, "Must be" ); |
158 | tracker->record_alloc_latency(_size, _alloc_type, duration_us); |
159 | if (duration_us > ShenandoahAllocationStallThreshold) { |
160 | log_warning(gc)("Allocation stall: %.0f us (threshold: " INTX_FORMAT " us)" , |
161 | duration_us, ShenandoahAllocationStallThreshold); |
162 | } |
163 | } |
164 | } |
165 | |
166 | ShenandoahWorkerSession::ShenandoahWorkerSession(uint worker_id) : _worker_id(worker_id) { |
167 | Thread* thr = Thread::current(); |
168 | assert(ShenandoahThreadLocalData::worker_id(thr) == ShenandoahThreadLocalData::INVALID_WORKER_ID, "Already set" ); |
169 | ShenandoahThreadLocalData::set_worker_id(thr, worker_id); |
170 | } |
171 | |
172 | ShenandoahConcurrentWorkerSession::~ShenandoahConcurrentWorkerSession() { |
173 | _event.commit(GCId::current(), ShenandoahPhaseTimings::phase_name(ShenandoahGCPhase::current_phase())); |
174 | } |
175 | |
176 | ShenandoahParallelWorkerSession::~ShenandoahParallelWorkerSession() { |
177 | _event.commit(GCId::current(), _worker_id, ShenandoahPhaseTimings::phase_name(ShenandoahGCPhase::current_phase())); |
178 | } |
179 | ShenandoahWorkerSession::~ShenandoahWorkerSession() { |
180 | #ifdef ASSERT |
181 | Thread* thr = Thread::current(); |
182 | assert(ShenandoahThreadLocalData::worker_id(thr) != ShenandoahThreadLocalData::INVALID_WORKER_ID, "Must be set" ); |
183 | ShenandoahThreadLocalData::set_worker_id(thr, ShenandoahThreadLocalData::INVALID_WORKER_ID); |
184 | #endif |
185 | } |
186 | |
187 | struct PhaseMap { |
188 | WeakProcessorPhases::Phase _weak_processor_phase; |
189 | ShenandoahPhaseTimings::GCParPhases _shenandoah_phase; |
190 | }; |
191 | |
192 | static const struct PhaseMap phase_mapping[] = { |
193 | #if INCLUDE_JVMTI |
194 | {WeakProcessorPhases::jvmti, ShenandoahPhaseTimings::JVMTIWeakRoots}, |
195 | #endif |
196 | #if INCLUDE_JFR |
197 | {WeakProcessorPhases::jfr, ShenandoahPhaseTimings::JFRWeakRoots}, |
198 | #endif |
199 | {WeakProcessorPhases::jni, ShenandoahPhaseTimings::JNIWeakRoots}, |
200 | {WeakProcessorPhases::stringtable, ShenandoahPhaseTimings::StringTableRoots}, |
201 | {WeakProcessorPhases::resolved_method_table, ShenandoahPhaseTimings::ResolvedMethodTableRoots}, |
202 | {WeakProcessorPhases::vm, ShenandoahPhaseTimings::VMWeakRoots} |
203 | }; |
204 | |
205 | STATIC_ASSERT(sizeof(phase_mapping) / sizeof(PhaseMap) == WeakProcessorPhases::phase_count); |
206 | |
207 | void ShenandoahTimingConverter::weak_processing_timing_to_shenandoah_timing(WeakProcessorPhaseTimes* weak_processing_timings, |
208 | ShenandoahWorkerTimings* sh_worker_times) { |
209 | assert(weak_processing_timings->max_threads() == weak_processing_timings->max_threads(), "Must match" ); |
210 | for (uint index = 0; index < WeakProcessorPhases::phase_count; index ++) { |
211 | weak_processing_phase_to_shenandoah_phase(phase_mapping[index]._weak_processor_phase, |
212 | weak_processing_timings, |
213 | phase_mapping[index]._shenandoah_phase, |
214 | sh_worker_times); |
215 | } |
216 | } |
217 | |
218 | void ShenandoahTimingConverter::weak_processing_phase_to_shenandoah_phase(WeakProcessorPhases::Phase wpp, |
219 | WeakProcessorPhaseTimes* weak_processing_timings, |
220 | ShenandoahPhaseTimings::GCParPhases spp, |
221 | ShenandoahWorkerTimings* sh_worker_times) { |
222 | if (WeakProcessorPhases::is_serial(wpp)) { |
223 | sh_worker_times->record_time_secs(spp, 0, weak_processing_timings->phase_time_sec(wpp)); |
224 | } else { |
225 | for (uint index = 0; index < weak_processing_timings->max_threads(); index ++) { |
226 | sh_worker_times->record_time_secs(spp, index, weak_processing_timings->worker_time_sec(index, wpp)); |
227 | } |
228 | } |
229 | } |
230 | |