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 : #include "gc/Barrier-inl.h"
55 :
56 : namespace js {
57 : namespace gcstats {
58 :
59 : /* Except for the first and last, slices of less than 12ms are not reported. */
60 : static const int64_t SLICE_MIN_REPORT_TIME = 12 * PRMJ_USEC_PER_MSEC;
61 :
62 : class StatisticsSerializer
63 0 : {
64 : typedef Vector<char, 128, SystemAllocPolicy> CharBuffer;
65 : CharBuffer buf_;
66 : bool asJSON_;
67 : bool needComma_;
68 : bool oom_;
69 :
70 : const static int MaxFieldValueLength = 128;
71 :
72 : public:
73 : enum Mode {
74 : AsJSON = true,
75 : AsText = false
76 : };
77 :
78 0 : StatisticsSerializer(Mode asJSON)
79 0 : : buf_(), asJSON_(asJSON), needComma_(false), oom_(false)
80 0 : {}
81 :
82 0 : bool isJSON() { return asJSON_; }
83 :
84 0 : bool isOOM() { return oom_; }
85 :
86 0 : void endLine() {
87 0 : if (!asJSON_) {
88 0 : p("\n");
89 0 : needComma_ = false;
90 : }
91 0 : }
92 :
93 0 : void extra(const char *str) {
94 0 : if (!asJSON_) {
95 0 : needComma_ = false;
96 0 : p(str);
97 : }
98 0 : }
99 :
100 0 : void appendString(const char *name, const char *value) {
101 0 : put(name, value, "", true);
102 0 : }
103 :
104 0 : void appendNumber(const char *name, const char *vfmt, const char *units, ...) {
105 : va_list va;
106 0 : va_start(va, units);
107 0 : append(name, vfmt, va, units);
108 0 : va_end(va);
109 0 : }
110 :
111 0 : void appendIfNonzeroMS(const char *name, double v) {
112 0 : if (asJSON_ || v >= 0.1)
113 0 : appendNumber(name, "%.1f", "ms", v);
114 0 : }
115 :
116 0 : void beginObject(const char *name) {
117 0 : if (needComma_)
118 0 : pJSON(", ");
119 0 : if (asJSON_ && name) {
120 0 : putQuoted(name);
121 0 : pJSON(": ");
122 : }
123 0 : pJSON("{");
124 0 : needComma_ = false;
125 0 : }
126 :
127 0 : void endObject() {
128 0 : needComma_ = false;
129 0 : pJSON("}");
130 0 : needComma_ = true;
131 0 : }
132 :
133 0 : void beginArray(const char *name) {
134 0 : if (needComma_)
135 0 : pJSON(", ");
136 0 : if (asJSON_)
137 0 : putQuoted(name);
138 0 : pJSON(": [");
139 0 : needComma_ = false;
140 0 : }
141 :
142 0 : void endArray() {
143 0 : needComma_ = false;
144 0 : pJSON("]");
145 0 : needComma_ = true;
146 0 : }
147 :
148 0 : jschar *finishJSString() {
149 0 : char *buf = finishCString();
150 0 : if (!buf)
151 0 : return NULL;
152 :
153 0 : size_t nchars = strlen(buf);
154 0 : jschar *out = (jschar *)js_malloc(sizeof(jschar) * (nchars + 1));
155 0 : if (!out) {
156 0 : oom_ = true;
157 0 : js_free(buf);
158 0 : return NULL;
159 : }
160 :
161 0 : size_t outlen = nchars;
162 0 : bool ok = InflateStringToBuffer(NULL, buf, nchars, out, &outlen);
163 0 : js_free(buf);
164 0 : if (!ok) {
165 0 : oom_ = true;
166 0 : js_free(out);
167 0 : return NULL;
168 : }
169 0 : out[nchars] = 0;
170 :
171 0 : return out;
172 : }
173 :
174 0 : char *finishCString() {
175 0 : if (oom_)
176 0 : return NULL;
177 :
178 0 : buf_.append('\0');
179 :
180 0 : char *buf = buf_.extractRawBuffer();
181 0 : if (!buf)
182 0 : oom_ = true;
183 :
184 0 : return buf;
185 : }
186 :
187 : private:
188 0 : void append(const char *name, const char *vfmt,
189 : va_list va, const char *units)
190 : {
191 : char val[MaxFieldValueLength];
192 0 : JS_vsnprintf(val, MaxFieldValueLength, vfmt, va);
193 0 : put(name, val, units, false);
194 0 : }
195 :
196 0 : void p(const char *cstr) {
197 0 : if (oom_)
198 0 : return;
199 :
200 0 : if (!buf_.append(cstr, strlen(cstr)))
201 0 : oom_ = true;
202 : }
203 :
204 0 : void p(const char c) {
205 0 : if (oom_)
206 0 : return;
207 :
208 0 : if (!buf_.append(c))
209 0 : oom_ = true;
210 : }
211 :
212 0 : void pJSON(const char *str) {
213 0 : if (asJSON_)
214 0 : p(str);
215 0 : }
216 :
217 0 : void put(const char *name, const char *val, const char *units, bool valueIsQuoted) {
218 0 : if (needComma_)
219 0 : p(", ");
220 0 : needComma_ = true;
221 :
222 0 : putKey(name);
223 0 : p(": ");
224 0 : if (valueIsQuoted)
225 0 : putQuoted(val);
226 : else
227 0 : p(val);
228 0 : if (!asJSON_)
229 0 : p(units);
230 0 : }
231 :
232 0 : void putQuoted(const char *str) {
233 0 : pJSON("\"");
234 0 : p(str);
235 0 : pJSON("\"");
236 0 : }
237 :
238 0 : void putKey(const char *str) {
239 0 : if (!asJSON_) {
240 0 : p(str);
241 0 : return;
242 : }
243 :
244 0 : p("\"");
245 0 : const char *c = str;
246 0 : while (*c) {
247 0 : if (*c == ' ' || *c == '\t')
248 0 : p('_');
249 0 : else if (isupper(*c))
250 0 : p(tolower(*c));
251 0 : else if (*c == '+')
252 0 : p("added_");
253 0 : else if (*c == '-')
254 0 : p("removed_");
255 0 : else if (*c != '(' && *c != ')')
256 0 : p(*c);
257 0 : c++;
258 : }
259 0 : p("\"");
260 : }
261 : };
262 :
263 : static const char *
264 0 : ExplainReason(gcreason::Reason reason)
265 : {
266 0 : switch (reason) {
267 : #define SWITCH_REASON(name) \
268 : case gcreason::name: \
269 : return #name;
270 0 : GCREASONS(SWITCH_REASON)
271 :
272 : default:
273 0 : JS_NOT_REACHED("bad GC reason");
274 : return "?";
275 : #undef SWITCH_REASON
276 : }
277 : }
278 :
279 : static double
280 0 : t(int64_t t)
281 : {
282 0 : return double(t) / PRMJ_USEC_PER_MSEC;
283 : }
284 :
285 : static void
286 0 : formatPhases(StatisticsSerializer &ss, const char *name, int64_t *times)
287 : {
288 0 : ss.beginObject(name);
289 0 : ss.appendIfNonzeroMS("Begin Callback", t(times[PHASE_GC_BEGIN]));
290 0 : ss.appendIfNonzeroMS("Wait Background Thread", t(times[PHASE_WAIT_BACKGROUND_THREAD]));
291 0 : ss.appendIfNonzeroMS("Purge", t(times[PHASE_PURGE]));
292 0 : ss.appendIfNonzeroMS("Mark", t(times[PHASE_MARK]));
293 0 : ss.appendIfNonzeroMS("Mark Roots", t(times[PHASE_MARK_ROOTS]));
294 0 : ss.appendIfNonzeroMS("Mark Delayed", t(times[PHASE_MARK_DELAYED]));
295 0 : ss.appendIfNonzeroMS("Mark Other", t(times[PHASE_MARK_OTHER]));
296 0 : ss.appendIfNonzeroMS("Finalize Start Callback", t(times[PHASE_FINALIZE_START]));
297 0 : ss.appendIfNonzeroMS("Sweep", t(times[PHASE_SWEEP]));
298 0 : ss.appendIfNonzeroMS("Sweep Compartments", t(times[PHASE_SWEEP_COMPARTMENTS]));
299 0 : ss.appendIfNonzeroMS("Sweep Object", t(times[PHASE_SWEEP_OBJECT]));
300 0 : ss.appendIfNonzeroMS("Sweep String", t(times[PHASE_SWEEP_STRING]));
301 0 : ss.appendIfNonzeroMS("Sweep Script", t(times[PHASE_SWEEP_SCRIPT]));
302 0 : ss.appendIfNonzeroMS("Sweep Shape", t(times[PHASE_SWEEP_SHAPE]));
303 0 : ss.appendIfNonzeroMS("Discard Code", t(times[PHASE_DISCARD_CODE]));
304 0 : ss.appendIfNonzeroMS("Discard Analysis", t(times[PHASE_DISCARD_ANALYSIS]));
305 0 : ss.appendIfNonzeroMS("Discard TI", t(times[PHASE_DISCARD_TI]));
306 0 : ss.appendIfNonzeroMS("Sweep Types", t(times[PHASE_SWEEP_TYPES]));
307 0 : ss.appendIfNonzeroMS("Clear Script Analysis", t(times[PHASE_CLEAR_SCRIPT_ANALYSIS]));
308 0 : ss.appendIfNonzeroMS("Finalize End Callback", t(times[PHASE_FINALIZE_END]));
309 0 : ss.appendIfNonzeroMS("Deallocate", t(times[PHASE_DESTROY]));
310 0 : ss.appendIfNonzeroMS("End Callback", t(times[PHASE_GC_END]));
311 0 : ss.endObject();
312 0 : }
313 :
314 : bool
315 0 : Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
316 : {
317 0 : int64_t total = 0, longest = 0;
318 0 : for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) {
319 0 : total += slice->duration();
320 0 : if (slice->duration() > longest)
321 0 : longest = slice->duration();
322 : }
323 :
324 0 : double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
325 0 : double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
326 :
327 0 : ss.beginObject(NULL);
328 0 : if (ss.isJSON())
329 0 : ss.appendNumber("Timestamp", "%llu", "", (unsigned long long)timestamp);
330 0 : ss.appendNumber("Total Time", "%.1f", "ms", t(total));
331 0 : ss.appendNumber("Compartments Collected", "%d", "", collectedCount);
332 0 : ss.appendNumber("Total Compartments", "%d", "", compartmentCount);
333 0 : ss.appendNumber("MMU (20ms)", "%d", "%", int(mmu20 * 100));
334 0 : ss.appendNumber("MMU (50ms)", "%d", "%", int(mmu50 * 100));
335 0 : if (slices.length() > 1 || ss.isJSON())
336 0 : ss.appendNumber("Max Pause", "%.1f", "ms", t(longest));
337 : else
338 0 : ss.appendString("Reason", ExplainReason(slices[0].reason));
339 0 : if (nonincrementalReason || ss.isJSON()) {
340 : ss.appendString("Nonincremental Reason",
341 0 : nonincrementalReason ? nonincrementalReason : "none");
342 : }
343 0 : ss.appendNumber("Allocated", "%u", "MB", unsigned(preBytes / 1024 / 1024));
344 0 : ss.appendNumber("+Chunks", "%d", "", counts[STAT_NEW_CHUNK]);
345 0 : ss.appendNumber("-Chunks", "%d", "", counts[STAT_DESTROY_CHUNK]);
346 0 : ss.endLine();
347 :
348 0 : if (slices.length() > 1 || ss.isJSON()) {
349 0 : ss.beginArray("Slices");
350 0 : for (size_t i = 0; i < slices.length(); i++) {
351 0 : int64_t width = slices[i].duration();
352 0 : if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME &&
353 0 : !slices[i].resetReason && !ss.isJSON())
354 : {
355 0 : continue;
356 : }
357 :
358 0 : ss.beginObject(NULL);
359 0 : ss.extra(" ");
360 0 : ss.appendNumber("Slice", "%d", "", i);
361 0 : ss.appendNumber("Time", "%.1f", "ms", t(slices[i].end - slices[0].start));
362 0 : ss.extra(" (");
363 0 : ss.appendNumber("Pause", "%.1f", "", t(width));
364 0 : ss.appendString("Reason", ExplainReason(slices[i].reason));
365 0 : if (slices[i].resetReason)
366 0 : ss.appendString("Reset", slices[i].resetReason);
367 0 : ss.extra("): ");
368 0 : formatPhases(ss, "times", slices[i].phaseTimes);
369 0 : ss.endLine();
370 0 : ss.endObject();
371 : }
372 0 : ss.endArray();
373 : }
374 0 : ss.extra(" Totals: ");
375 0 : formatPhases(ss, "totals", phaseTimes);
376 0 : ss.endObject();
377 :
378 0 : return !ss.isOOM();
379 : }
380 :
381 : jschar *
382 0 : Statistics::formatMessage()
383 : {
384 0 : StatisticsSerializer ss(StatisticsSerializer::AsText);
385 0 : formatData(ss, 0);
386 0 : return ss.finishJSString();
387 : }
388 :
389 : jschar *
390 0 : Statistics::formatJSON(uint64_t timestamp)
391 : {
392 0 : StatisticsSerializer ss(StatisticsSerializer::AsJSON);
393 0 : formatData(ss, timestamp);
394 0 : return ss.finishJSString();
395 : }
396 :
397 18761 : Statistics::Statistics(JSRuntime *rt)
398 : : runtime(rt),
399 18761 : startupTime(PRMJ_Now()),
400 : fp(NULL),
401 : fullFormat(false),
402 : collectedCount(0),
403 : compartmentCount(0),
404 37522 : nonincrementalReason(NULL)
405 : {
406 18761 : PodArrayZero(phaseTotals);
407 18761 : PodArrayZero(counts);
408 :
409 18761 : char *env = getenv("MOZ_GCTIMER");
410 18761 : if (!env || strcmp(env, "none") == 0) {
411 18761 : fp = NULL;
412 18761 : return;
413 : }
414 :
415 0 : if (strcmp(env, "stdout") == 0) {
416 0 : fullFormat = false;
417 0 : fp = stdout;
418 0 : } else if (strcmp(env, "stderr") == 0) {
419 0 : fullFormat = false;
420 0 : fp = stderr;
421 : } else {
422 0 : fullFormat = true;
423 :
424 0 : fp = fopen(env, "a");
425 0 : JS_ASSERT(fp);
426 : }
427 : }
428 :
429 37522 : Statistics::~Statistics()
430 : {
431 18761 : if (fp) {
432 0 : if (fullFormat) {
433 0 : StatisticsSerializer ss(StatisticsSerializer::AsText);
434 0 : formatPhases(ss, "", phaseTotals);
435 0 : char *msg = ss.finishCString();
436 0 : if (msg) {
437 0 : fprintf(fp, "TOTALS\n%s\n\n-------\n", msg);
438 0 : js_free(msg);
439 : }
440 : }
441 :
442 0 : if (fp != stdout && fp != stderr)
443 0 : fclose(fp);
444 : }
445 18761 : }
446 :
447 : int64_t
448 0 : Statistics::gcDuration()
449 : {
450 0 : return slices.back().end - slices[0].start;
451 : }
452 :
453 : void
454 0 : Statistics::printStats()
455 : {
456 0 : if (fullFormat) {
457 0 : StatisticsSerializer ss(StatisticsSerializer::AsText);
458 0 : formatData(ss, 0);
459 0 : char *msg = ss.finishCString();
460 0 : if (msg) {
461 0 : fprintf(fp, "GC(T+%.3fs) %s\n", t(slices[0].start - startupTime) / 1000.0, msg);
462 0 : js_free(msg);
463 : }
464 : } else {
465 : fprintf(fp, "%f %f %f\n",
466 : t(gcDuration()),
467 : t(phaseTimes[PHASE_MARK]),
468 0 : t(phaseTimes[PHASE_SWEEP]));
469 : }
470 0 : fflush(fp);
471 0 : }
472 :
473 : void
474 38427 : Statistics::beginGC()
475 : {
476 38427 : PodArrayZero(phaseStarts);
477 38427 : PodArrayZero(phaseTimes);
478 :
479 38427 : slices.clearAndFree();
480 38427 : nonincrementalReason = NULL;
481 :
482 38427 : preBytes = runtime->gcBytes;
483 :
484 38427 : Probes::GCStart();
485 38427 : }
486 :
487 : void
488 38427 : Statistics::endGC()
489 : {
490 38427 : Probes::GCEnd();
491 38427 : crash::SnapshotGCStack();
492 :
493 883821 : for (int i = 0; i < PHASE_LIMIT; i++)
494 845394 : phaseTotals[i] += phaseTimes[i];
495 :
496 38427 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
497 0 : (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, collectedCount == compartmentCount ? 0 : 1);
498 0 : (*cb)(JS_TELEMETRY_GC_MS, t(gcDuration()));
499 0 : (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK]));
500 0 : (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP]));
501 0 : (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
502 0 : (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
503 :
504 0 : double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
505 0 : (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
506 : }
507 :
508 38427 : if (fp)
509 0 : printStats();
510 38427 : }
511 :
512 : void
513 38481 : Statistics::beginSlice(int collectedCount, int compartmentCount, gcreason::Reason reason)
514 : {
515 38481 : this->collectedCount = collectedCount;
516 38481 : this->compartmentCount = compartmentCount;
517 :
518 38481 : bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
519 38481 : if (first)
520 38427 : beginGC();
521 :
522 38481 : SliceData data(reason, PRMJ_Now());
523 38481 : (void) slices.append(data); /* Ignore any OOMs here. */
524 :
525 38481 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
526 0 : (*cb)(JS_TELEMETRY_GC_REASON, reason);
527 :
528 38481 : bool wasFullGC = collectedCount == compartmentCount;
529 38481 : if (GCSliceCallback cb = runtime->gcSliceCallback)
530 0 : (*cb)(runtime, first ? GC_CYCLE_BEGIN : GC_SLICE_BEGIN, GCDescription(!wasFullGC));
531 38481 : }
532 :
533 : void
534 38481 : Statistics::endSlice()
535 : {
536 38481 : slices.back().end = PRMJ_Now();
537 :
538 38481 : if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
539 0 : (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
540 0 : (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
541 : }
542 :
543 38481 : bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
544 38481 : if (last)
545 38427 : endGC();
546 :
547 38481 : bool wasFullGC = collectedCount == compartmentCount;
548 38481 : if (GCSliceCallback cb = runtime->gcSliceCallback) {
549 0 : if (last)
550 0 : (*cb)(runtime, GC_CYCLE_END, GCDescription(!wasFullGC));
551 : else
552 0 : (*cb)(runtime, GC_SLICE_END, GCDescription(!wasFullGC));
553 : }
554 :
555 : /* Do this after the slice callback since it uses these values. */
556 38481 : if (last)
557 38427 : PodArrayZero(counts);
558 38481 : }
559 :
560 : void
561 1051317 : Statistics::beginPhase(Phase phase)
562 : {
563 1051317 : phaseStarts[phase] = PRMJ_Now();
564 :
565 1051317 : if (phase == gcstats::PHASE_MARK)
566 115362 : Probes::GCStartMarkPhase();
567 935955 : else if (phase == gcstats::PHASE_SWEEP)
568 38427 : Probes::GCStartSweepPhase();
569 1051317 : }
570 :
571 : void
572 1051317 : Statistics::endPhase(Phase phase)
573 : {
574 1051317 : int64_t now = PRMJ_Now();
575 1051317 : int64_t t = now - phaseStarts[phase];
576 1051317 : slices.back().phaseTimes[phase] += t;
577 1051317 : phaseTimes[phase] += t;
578 :
579 1051317 : if (phase == gcstats::PHASE_MARK)
580 115362 : Probes::GCEndMarkPhase();
581 935955 : else if (phase == gcstats::PHASE_SWEEP)
582 38427 : Probes::GCEndSweepPhase();
583 1051317 : }
584 :
585 : /*
586 : * MMU (minimum mutator utilization) is a measure of how much garbage collection
587 : * is affecting the responsiveness of the system. MMU measurements are given
588 : * with respect to a certain window size. If we report MMU(50ms) = 80%, then
589 : * that means that, for any 50ms window of time, at least 80% of the window is
590 : * devoted to the mutator. In other words, the GC is running for at most 20% of
591 : * the window, or 10ms. The GC can run multiple slices during the 50ms window
592 : * as long as the total time it spends is at most 10ms.
593 : */
594 : double
595 0 : Statistics::computeMMU(int64_t window)
596 : {
597 0 : JS_ASSERT(!slices.empty());
598 :
599 0 : int64_t gc = slices[0].end - slices[0].start;
600 0 : int64_t gcMax = gc;
601 :
602 0 : if (gc >= window)
603 0 : return 0.0;
604 :
605 0 : int startIndex = 0;
606 0 : for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) {
607 0 : gc += slices[endIndex].end - slices[endIndex].start;
608 :
609 0 : while (slices[endIndex].end - slices[startIndex].end >= window) {
610 0 : gc -= slices[startIndex].end - slices[startIndex].start;
611 0 : startIndex++;
612 : }
613 :
614 0 : int64_t cur = gc;
615 0 : if (slices[endIndex].end - slices[startIndex].start > window)
616 0 : cur -= (slices[endIndex].end - slices[startIndex].start - window);
617 0 : if (cur > gcMax)
618 0 : gcMax = cur;
619 : }
620 :
621 0 : return double(window - gcMax) / window;
622 : }
623 :
624 : } /* namespace gcstats */
625 : } /* namespace js */
|