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
38ShenandoahPhaseTimings::Phase ShenandoahGCPhase::_current_phase = ShenandoahGCPhase::_invalid_phase;
39
40ShenandoahGCSession::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
66ShenandoahGCSession::~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
76ShenandoahGCPauseMark::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
96ShenandoahGCPauseMark::~ShenandoahGCPauseMark() {
97 _heap->gc_timer()->register_gc_phase_end(Ticks::now());
98 _heap->heuristics()->record_gc_end();
99}
100
101ShenandoahGCPhase::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
113ShenandoahGCPhase::~ShenandoahGCPhase() {
114 _heap->phase_timings()->record_phase_end(_phase);
115 _current_phase = _parent_phase;
116}
117
118bool ShenandoahGCPhase::is_valid_phase(ShenandoahPhaseTimings::Phase phase) {
119 return phase >= 0 && phase < ShenandoahPhaseTimings::_num_phases;
120}
121
122bool 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
139ShenandoahAllocTrace::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
151ShenandoahAllocTrace::~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
166ShenandoahWorkerSession::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
172ShenandoahConcurrentWorkerSession::~ShenandoahConcurrentWorkerSession() {
173 _event.commit(GCId::current(), ShenandoahPhaseTimings::phase_name(ShenandoahGCPhase::current_phase()));
174}
175
176ShenandoahParallelWorkerSession::~ShenandoahParallelWorkerSession() {
177 _event.commit(GCId::current(), _worker_id, ShenandoahPhaseTimings::phase_name(ShenandoahGCPhase::current_phase()));
178}
179ShenandoahWorkerSession::~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
187struct PhaseMap {
188 WeakProcessorPhases::Phase _weak_processor_phase;
189 ShenandoahPhaseTimings::GCParPhases _shenandoah_phase;
190};
191
192static 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
205STATIC_ASSERT(sizeof(phase_mapping) / sizeof(PhaseMap) == WeakProcessorPhases::phase_count);
206
207void 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
218void 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