1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey JavaScript engine.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include <stdio.h>
41 : #include <stdarg.h>
42 :
43 : #include "jscntxt.h"
44 : #include "jscompartment.h"
45 : #include "jscrashformat.h"
46 : #include "jscrashreport.h"
47 : #include "jsprf.h"
48 : #include "jsprobes.h"
49 : #include "jsutil.h"
50 : #include "prmjtime.h"
51 :
52 : #include "gc/Statistics.h"
53 :
54 : namespace js {
55 : namespace gcstats {
56 :
57 : static const char *
58 4446 : ExplainReason(gcreason::Reason reason)
59 : {
60 4446 : switch (reason) {
61 : #define SWITCH_REASON(name) \
62 : case gcreason::name: \
63 : return #name;
64 2164 : GCREASONS(SWITCH_REASON)
65 :
66 : default:
67 0 : JS_NOT_REACHED("bad GC reason");
68 : return "?";
69 : #undef SWITCH_REASON
70 : }
71 : }
72 :
73 : void
74 98694 : Statistics::fmt(const char *f, ...)
75 : {
76 : va_list va;
77 98694 : size_t off = strlen(buffer);
78 :
79 98694 : va_start(va, f);
80 98694 : JS_vsnprintf(buffer + off, BUFFER_SIZE - off, f, va);
81 98694 : va_end(va);
82 98694 : }
83 :
84 : void
85 57798 : Statistics::fmtIfNonzero(const char *name, double t)
86 : {
87 57798 : if (t) {
88 38232 : if (needComma)
89 33786 : fmt(", ");
90 38232 : fmt("%s: %.1f", name, t);
91 38232 : needComma = true;
92 : }
93 57798 : }
94 :
95 : void
96 4446 : Statistics::formatPhases(int64_t *times)
97 : {
98 4446 : needComma = false;
99 4446 : fmtIfNonzero("mark", t(times[PHASE_MARK]));
100 4446 : fmtIfNonzero("mark-roots", t(times[PHASE_MARK_ROOTS]));
101 4446 : fmtIfNonzero("mark-delayed", t(times[PHASE_MARK_DELAYED]));
102 4446 : fmtIfNonzero("mark-other", t(times[PHASE_MARK_OTHER]));
103 4446 : fmtIfNonzero("sweep", t(times[PHASE_SWEEP]));
104 4446 : fmtIfNonzero("sweep-obj", t(times[PHASE_SWEEP_OBJECT]));
105 4446 : fmtIfNonzero("sweep-string", t(times[PHASE_SWEEP_STRING]));
106 4446 : fmtIfNonzero("sweep-script", t(times[PHASE_SWEEP_SCRIPT]));
107 4446 : fmtIfNonzero("sweep-shape", t(times[PHASE_SWEEP_SHAPE]));
108 4446 : fmtIfNonzero("discard-code", t(times[PHASE_DISCARD_CODE]));
109 4446 : fmtIfNonzero("discard-analysis", t(times[PHASE_DISCARD_ANALYSIS]));
110 4446 : fmtIfNonzero("xpconnect", t(times[PHASE_XPCONNECT]));
111 4446 : fmtIfNonzero("deallocate", t(times[PHASE_DESTROY]));
112 4446 : }
113 :
114 : /* Except for the first and last, slices of less than 12ms are not reported. */
115 : static const int64_t SLICE_MIN_REPORT_TIME = 12 * PRMJ_USEC_PER_MSEC;
116 :
117 : const char *
118 4446 : Statistics::formatData()
119 : {
120 4446 : buffer[0] = 0x00;
121 :
122 4446 : int64_t total = 0, longest = 0;
123 :
124 8892 : for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) {
125 4446 : total += slice->duration();
126 4446 : if (slice->duration() > longest)
127 4446 : longest = slice->duration();
128 : }
129 :
130 4446 : double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
131 4446 : double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
132 :
133 4446 : fmt("TotalTime: %.1fms, Type: %s", t(total), compartment ? "compartment" : "global");
134 4446 : fmt(", MMU(20ms): %d%%, MMU(50ms): %d%%", int(mmu20 * 100), int(mmu50 * 100));
135 :
136 4446 : if (slices.length() > 1)
137 0 : fmt(", MaxPause: %.1f", t(longest));
138 : else
139 4446 : fmt(", Reason: %s", ExplainReason(slices[0].reason));
140 :
141 4446 : if (nonincrementalReason)
142 4446 : fmt(", NonIncrementalReason: %s", nonincrementalReason);
143 :
144 4446 : fmt(", +chunks: %d, -chunks: %d\n", counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]);
145 :
146 4446 : if (slices.length() > 1) {
147 0 : for (size_t i = 0; i < slices.length(); i++) {
148 0 : int64_t width = slices[i].duration();
149 0 : if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME &&
150 0 : !slices[i].resetReason)
151 : {
152 0 : continue;
153 : }
154 :
155 : fmt(" Slice %d @ %.1fms (Pause: %.1f, Reason: %s",
156 : i,
157 0 : t(slices[i].end - slices[0].start),
158 : t(width),
159 0 : ExplainReason(slices[i].reason));
160 0 : if (slices[i].resetReason)
161 0 : fmt(", Reset: %s", slices[i].resetReason);
162 0 : fmt("): ");
163 0 : formatPhases(slices[i].phaseTimes);
164 0 : fmt("\n");
165 : }
166 :
167 0 : fmt(" Totals: ");
168 : }
169 :
170 4446 : formatPhases(phaseTimes);
171 4446 : fmt("\n");
172 :
173 4446 : return buffer;
174 : }
175 :
176 19910 : Statistics::Statistics(JSRuntime *rt)
177 : : runtime(rt),
178 19910 : startupTime(PRMJ_Now()),
179 : fp(NULL),
180 : fullFormat(false),
181 : compartment(NULL),
182 : nonincrementalReason(NULL),
183 39820 : needComma(false)
184 : {
185 19910 : PodArrayZero(phaseTotals);
186 19910 : PodArrayZero(counts);
187 :
188 19910 : char *env = getenv("MOZ_GCTIMER");
189 19910 : if (!env || strcmp(env, "none") == 0) {
190 19910 : fp = NULL;
191 19910 : return;
192 : }
193 :
194 0 : if (strcmp(env, "stdout") == 0) {
195 0 : fullFormat = false;
196 0 : fp = stdout;
197 0 : } else if (strcmp(env, "stderr") == 0) {
198 0 : fullFormat = false;
199 0 : fp = stderr;
200 : } else {
201 0 : fullFormat = true;
202 :
203 0 : fp = fopen(env, "a");
204 0 : JS_ASSERT(fp);
205 : }
206 : }
207 :
208 39816 : Statistics::~Statistics()
209 : {
210 19908 : if (fp) {
211 0 : if (fullFormat) {
212 0 : buffer[0] = 0x00;
213 0 : formatPhases(phaseTotals);
214 0 : fprintf(fp, "TOTALS\n%s\n\n-------\n", buffer);
215 : }
216 :
217 0 : if (fp != stdout && fp != stderr)
218 0 : fclose(fp);
219 : }
220 19908 : }
221 :
222 : double
223 121156 : Statistics::t(int64_t t)
224 : {
225 121156 : return double(t) / PRMJ_USEC_PER_MSEC;
226 : }
227 :
228 : int64_t
229 14728 : Statistics::gcDuration()
230 : {
231 14728 : return slices.back().end - slices[0].start;
232 : }
233 :
234 : void
235 0 : Statistics::printStats()
236 : {
237 0 : if (fullFormat) {
238 : fprintf(fp, "GC(T+%.3fs) %s\n",
239 0 : t(slices[0].start - startupTime) / 1000.0,
240 0 : formatData());
241 : } else {
242 : fprintf(fp, "%f %f %f\n",
243 : t(gcDuration()),
244 : t(phaseTimes[PHASE_MARK]),
245 0 : t(phaseTimes[PHASE_SWEEP]));
246 : }
247 0 : fflush(fp);
248 0 : }
249 :
250 : void
251 51092 : Statistics::beginGC()
252 : {
253 51092 : PodArrayZero(phaseStarts);
254 51092 : PodArrayZero(phaseTimes);
255 :
256 51092 : slices.clearAndFree();
257 51092 : nonincrementalReason = NULL;
258 :
259 51092 : Probes::GCStart();
260 51092 : }
261 :
262 : void
263 51092 : Statistics::endGC()
264 : {
265 51092 : Probes::GCEnd();
266 51092 : crash::SnapshotGCStack();
267 :
268 715288 : for (int i = 0; i < PHASE_LIMIT; i++)
269 664196 : phaseTotals[i] += phaseTimes[i];
270 :
271 51092 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
272 14728 : (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0);
273 14728 : (*cb)(JS_TELEMETRY_GC_MS, t(gcDuration()));
274 14728 : (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK]));
275 14728 : (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP]));
276 14728 : (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
277 14728 : (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
278 :
279 14728 : double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
280 14728 : (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
281 : }
282 :
283 51092 : if (fp)
284 0 : printStats();
285 :
286 51092 : PodArrayZero(counts);
287 51092 : }
288 :
289 : void
290 51092 : Statistics::beginSlice(JSCompartment *comp, gcreason::Reason reason)
291 : {
292 51092 : compartment = comp;
293 :
294 51092 : bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
295 51092 : if (first)
296 51092 : beginGC();
297 :
298 51092 : SliceData data(reason, PRMJ_Now());
299 51092 : (void) slices.append(data); /* Ignore any OOMs here. */
300 :
301 51092 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
302 14728 : (*cb)(JS_TELEMETRY_GC_REASON, reason);
303 :
304 51092 : if (GCSliceCallback cb = runtime->gcSliceCallback) {
305 4446 : GCDescription desc(NULL, !!compartment);
306 4446 : (*cb)(runtime, first ? GC_CYCLE_BEGIN : GC_SLICE_BEGIN, desc);
307 : }
308 51092 : }
309 :
310 : void
311 51092 : Statistics::endSlice()
312 : {
313 51092 : slices.back().end = PRMJ_Now();
314 :
315 51092 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
316 14728 : (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
317 14728 : (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
318 : }
319 :
320 51092 : bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
321 51092 : if (last)
322 51092 : endGC();
323 :
324 51092 : if (GCSliceCallback cb = runtime->gcSliceCallback) {
325 4446 : if (last)
326 4446 : (*cb)(runtime, GC_CYCLE_END, GCDescription(formatData(), !!compartment));
327 : else
328 0 : (*cb)(runtime, GC_SLICE_END, GCDescription(NULL, !!compartment));
329 : }
330 51092 : }
331 :
332 : void
333 858024 : Statistics::beginPhase(Phase phase)
334 : {
335 858024 : phaseStarts[phase] = PRMJ_Now();
336 :
337 858024 : if (phase == gcstats::PHASE_MARK)
338 153276 : Probes::GCStartMarkPhase();
339 704748 : else if (phase == gcstats::PHASE_SWEEP)
340 51092 : Probes::GCStartSweepPhase();
341 858024 : }
342 :
343 : void
344 858024 : Statistics::endPhase(Phase phase)
345 : {
346 858024 : int64_t now = PRMJ_Now();
347 858024 : int64_t t = now - phaseStarts[phase];
348 858024 : slices.back().phaseTimes[phase] += t;
349 858024 : phaseTimes[phase] += t;
350 :
351 858024 : if (phase == gcstats::PHASE_MARK)
352 153276 : Probes::GCEndMarkPhase();
353 704748 : else if (phase == gcstats::PHASE_SWEEP)
354 51092 : Probes::GCEndSweepPhase();
355 858024 : }
356 :
357 : /*
358 : * MMU (minimum mutator utilization) is a measure of how much garbage collection
359 : * is affecting the responsiveness of the system. MMU measurements are given
360 : * with respect to a certain window size. If we report MMU(50ms) = 80%, then
361 : * that means that, for any 50ms window of time, at least 80% of the window is
362 : * devoted to the mutator. In other words, the GC is running for at most 20% of
363 : * the window, or 10ms. The GC can run multiple slices during the 50ms window
364 : * as long as the total time it spends is at most 10ms.
365 : */
366 : double
367 23620 : Statistics::computeMMU(int64_t window)
368 : {
369 23620 : JS_ASSERT(!slices.empty());
370 :
371 23620 : int64_t gc = slices[0].end - slices[0].start;
372 23620 : int64_t gcMax = gc;
373 :
374 23620 : if (gc >= window)
375 3021 : return 0.0;
376 :
377 20599 : int startIndex = 0;
378 20599 : for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) {
379 0 : gc += slices[endIndex].end - slices[endIndex].start;
380 :
381 0 : while (slices[endIndex].end - slices[startIndex].end >= window) {
382 0 : gc -= slices[startIndex].end - slices[startIndex].start;
383 0 : startIndex++;
384 : }
385 :
386 0 : int64_t cur = gc;
387 0 : if (slices[endIndex].end - slices[startIndex].start > window)
388 0 : cur -= (slices[endIndex].end - slices[startIndex].start - window);
389 0 : if (cur > gcMax)
390 0 : gcMax = cur;
391 : }
392 :
393 20599 : return double(window - gcMax) / window;
394 : }
395 :
396 : } /* namespace gcstats */
397 : } /* namespace js */
|