1 /*
2 * Copyright Amazon.com Inc. 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 #include "precompiled.hpp"
25
26 #include "gc/shenandoah/shenandoahAsserts.hpp"
27 #include "gc/shenandoah/shenandoahMmuTracker.hpp"
28 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
29 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
30 #include "gc/shenandoah/shenandoahYoungGeneration.hpp"
31 #include "logging/log.hpp"
32 #include "runtime/os.hpp"
33 #include "runtime/task.hpp"
34
35 class ShenandoahMmuTask : public PeriodicTask {
36 ShenandoahMmuTracker* _mmu_tracker;
37 public:
38 explicit ShenandoahMmuTask(ShenandoahMmuTracker* mmu_tracker) :
39 PeriodicTask(GCPauseIntervalMillis), _mmu_tracker(mmu_tracker) {}
40
41 void task() override {
42 _mmu_tracker->report();
43 }
44 };
45
46 class ThreadTimeAccumulator : public ThreadClosure {
47 public:
48 size_t total_time;
49 ThreadTimeAccumulator() : total_time(0) {}
50 void do_thread(Thread* thread) override {
51 assert(!thread->has_terminated(), "Cannot get cpu time for terminated thread");
52 total_time += os::thread_cpu_time(thread);
53 }
54 };
55
56 ShenandoahMmuTracker::ShenandoahMmuTracker() :
57 _most_recent_timestamp(0.0),
58 _most_recent_gc_time(0.0),
59 _most_recent_gcu(0.0),
60 _most_recent_mutator_time(0.0),
61 _most_recent_mu(0.0),
62 _most_recent_periodic_time_stamp(0.0),
63 _most_recent_periodic_gc_time(0.0),
64 _most_recent_periodic_mutator_time(0.0),
65 _mmu_periodic_task(new ShenandoahMmuTask(this)) {
66 }
67
68 ShenandoahMmuTracker::~ShenandoahMmuTracker() {
69 delete _mmu_periodic_task;
70 }
71
72 void ShenandoahMmuTracker::fetch_cpu_times(double &gc_time, double &mutator_time) {
73 ThreadTimeAccumulator cl;
74 // We include only the gc threads because those are the only threads
75 // we are responsible for.
76 ShenandoahHeap::heap()->gc_threads_do(&cl);
77 double most_recent_gc_thread_time = double(cl.total_time) / NANOSECS_PER_SEC;
78 gc_time = most_recent_gc_thread_time;
79
80 double process_real_time(0.0), process_user_time(0.0), process_system_time(0.0);
81 bool valid = os::getTimesSecs(&process_real_time, &process_user_time, &process_system_time);
82 assert(valid, "don't know why this would not be valid");
83 mutator_time =(process_user_time + process_system_time) - most_recent_gc_thread_time;
84 }
85
86 void ShenandoahMmuTracker::update_utilization(size_t gcid, const char* msg) {
87 double current = os::elapsedTime();
88 _most_recent_gcid = gcid;
89 _most_recent_is_full = false;
90
91 if (gcid == 0) {
92 fetch_cpu_times(_most_recent_gc_time, _most_recent_mutator_time);
93
94 _most_recent_timestamp = current;
95 } else {
96 double gc_cycle_period = current - _most_recent_timestamp;
97 _most_recent_timestamp = current;
98
99 double gc_thread_time, mutator_thread_time;
100 fetch_cpu_times(gc_thread_time, mutator_thread_time);
101 double gc_time = gc_thread_time - _most_recent_gc_time;
102 _most_recent_gc_time = gc_thread_time;
103 _most_recent_gcu = gc_time / (_active_processors * gc_cycle_period);
104 double mutator_time = mutator_thread_time - _most_recent_mutator_time;
105 _most_recent_mutator_time = mutator_thread_time;
106 _most_recent_mu = mutator_time / (_active_processors * gc_cycle_period);
107 log_info(gc, ergo)("At end of %s: GCU: %.1f%%, MU: %.1f%% during period of %.3fs",
108 msg, _most_recent_gcu * 100, _most_recent_mu * 100, gc_cycle_period);
109 }
110 }
111
112 void ShenandoahMmuTracker::record_young(size_t gcid) {
113 update_utilization(gcid, "Concurrent Young GC");
114 }
115
116 void ShenandoahMmuTracker::record_global(size_t gcid) {
117 update_utilization(gcid, "Concurrent Global GC");
118 }
119
120 void ShenandoahMmuTracker::record_bootstrap(size_t gcid) {
121 // Not likely that this will represent an "ideal" GCU, but doesn't hurt to try
122 update_utilization(gcid, "Concurrent Bootstrap GC");
123 }
124
125 void ShenandoahMmuTracker::record_old_marking_increment(bool old_marking_done) {
126 // No special processing for old marking
127 double now = os::elapsedTime();
128 double duration = now - _most_recent_timestamp;
129
130 double gc_time, mutator_time;
131 fetch_cpu_times(gc_time, mutator_time);
132 double gcu = (gc_time - _most_recent_gc_time) / duration;
133 double mu = (mutator_time - _most_recent_mutator_time) / duration;
134 log_info(gc, ergo)("At end of %s: GCU: %.1f%%, MU: %.1f%% for duration %.3fs (totals to be subsumed in next gc report)",
135 old_marking_done? "last OLD marking increment": "OLD marking increment",
136 gcu * 100, mu * 100, duration);
137 }
138
139 void ShenandoahMmuTracker::record_mixed(size_t gcid) {
140 update_utilization(gcid, "Mixed Concurrent GC");
141 }
142
143 void ShenandoahMmuTracker::record_degenerated(size_t gcid, bool is_old_bootstrap) {
144 if ((gcid == _most_recent_gcid) && _most_recent_is_full) {
145 // Do nothing. This is a redundant recording for the full gc that just completed.
146 } else if (is_old_bootstrap) {
147 update_utilization(gcid, "Degenerated Bootstrap Old GC");
148 } else {
149 update_utilization(gcid, "Degenerated Young GC");
150 }
151 }
152
153 void ShenandoahMmuTracker::record_full(size_t gcid) {
154 update_utilization(gcid, "Full GC");
155 _most_recent_is_full = true;
156 }
157
158 void ShenandoahMmuTracker::report() {
159 // This is only called by the periodic thread.
160 double current = os::elapsedTime();
161 double time_delta = current - _most_recent_periodic_time_stamp;
162 _most_recent_periodic_time_stamp = current;
163
164 double gc_time, mutator_time;
165 fetch_cpu_times(gc_time, mutator_time);
166
167 double gc_delta = gc_time - _most_recent_periodic_gc_time;
168 _most_recent_periodic_gc_time = gc_time;
169
170 double mutator_delta = mutator_time - _most_recent_periodic_mutator_time;
171 _most_recent_periodic_mutator_time = mutator_time;
172
173 double mu = mutator_delta / (_active_processors * time_delta);
174 double gcu = gc_delta / (_active_processors * time_delta);
175 log_debug(gc)("Periodic Sample: GCU = %.3f%%, MU = %.3f%% during most recent %.1fs", gcu * 100, mu * 100, time_delta);
176 }
177
178 void ShenandoahMmuTracker::stop() const {
179 _mmu_periodic_task->disenroll();
180 }
181
182 void ShenandoahMmuTracker::initialize() {
183 // initialize static data
184 _active_processors = os::initial_active_processor_count();
185
186 _most_recent_periodic_time_stamp = os::elapsedTime();
187 fetch_cpu_times(_most_recent_periodic_gc_time, _most_recent_periodic_mutator_time);
188 _mmu_periodic_task->enroll();
189 }