js/src/gc/Statistics.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/gc/Statistics.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,698 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "gc/Statistics.h"
    1.11 +
    1.12 +#include "mozilla/PodOperations.h"
    1.13 +
    1.14 +#include <ctype.h>
    1.15 +#include <stdarg.h>
    1.16 +#include <stdio.h>
    1.17 +
    1.18 +#include "jscrashreport.h"
    1.19 +#include "jsprf.h"
    1.20 +#include "jsutil.h"
    1.21 +#include "prmjtime.h"
    1.22 +
    1.23 +#include "gc/Memory.h"
    1.24 +#include "vm/Runtime.h"
    1.25 +
    1.26 +using namespace js;
    1.27 +using namespace js::gcstats;
    1.28 +
    1.29 +using mozilla::PodArrayZero;
    1.30 +
    1.31 +/* Except for the first and last, slices of less than 42ms are not reported. */
    1.32 +static const int64_t SLICE_MIN_REPORT_TIME = 42 * PRMJ_USEC_PER_MSEC;
    1.33 +
    1.34 +class gcstats::StatisticsSerializer
    1.35 +{
    1.36 +    typedef Vector<char, 128, SystemAllocPolicy> CharBuffer;
    1.37 +    CharBuffer buf_;
    1.38 +    bool asJSON_;
    1.39 +    bool needComma_;
    1.40 +    bool oom_;
    1.41 +
    1.42 +    static const int MaxFieldValueLength = 128;
    1.43 +
    1.44 +  public:
    1.45 +    enum Mode {
    1.46 +        AsJSON = true,
    1.47 +        AsText = false
    1.48 +    };
    1.49 +
    1.50 +    StatisticsSerializer(Mode asJSON)
    1.51 +      : buf_(), asJSON_(asJSON), needComma_(false), oom_(false)
    1.52 +    {}
    1.53 +
    1.54 +    bool isJSON() { return asJSON_; }
    1.55 +
    1.56 +    bool isOOM() { return oom_; }
    1.57 +
    1.58 +    void endLine() {
    1.59 +        if (!asJSON_) {
    1.60 +            p("\n");
    1.61 +            needComma_ = false;
    1.62 +        }
    1.63 +    }
    1.64 +
    1.65 +    void extra(const char *str) {
    1.66 +        if (!asJSON_) {
    1.67 +            needComma_ = false;
    1.68 +            p(str);
    1.69 +        }
    1.70 +    }
    1.71 +
    1.72 +    void appendString(const char *name, const char *value) {
    1.73 +        put(name, value, "", true);
    1.74 +    }
    1.75 +
    1.76 +    void appendNumber(const char *name, const char *vfmt, const char *units, ...) {
    1.77 +        va_list va;
    1.78 +        va_start(va, units);
    1.79 +        append(name, vfmt, va, units);
    1.80 +        va_end(va);
    1.81 +    }
    1.82 +
    1.83 +    void appendDecimal(const char *name, const char *units, double d) {
    1.84 +        if (d < 0)
    1.85 +            d = 0;
    1.86 +        if (asJSON_)
    1.87 +            appendNumber(name, "%d.%d", units, (int)d, (int)(d * 10.) % 10);
    1.88 +        else
    1.89 +            appendNumber(name, "%.1f", units, d);
    1.90 +    }
    1.91 +
    1.92 +    void appendIfNonzeroMS(const char *name, double v) {
    1.93 +        if (asJSON_ || v >= 0.1)
    1.94 +            appendDecimal(name, "ms", v);
    1.95 +    }
    1.96 +
    1.97 +    void beginObject(const char *name) {
    1.98 +        if (needComma_)
    1.99 +            pJSON(", ");
   1.100 +        if (asJSON_ && name) {
   1.101 +            putKey(name);
   1.102 +            pJSON(": ");
   1.103 +        }
   1.104 +        pJSON("{");
   1.105 +        needComma_ = false;
   1.106 +    }
   1.107 +
   1.108 +    void endObject() {
   1.109 +        needComma_ = false;
   1.110 +        pJSON("}");
   1.111 +        needComma_ = true;
   1.112 +    }
   1.113 +
   1.114 +    void beginArray(const char *name) {
   1.115 +        if (needComma_)
   1.116 +            pJSON(", ");
   1.117 +        if (asJSON_)
   1.118 +            putKey(name);
   1.119 +        pJSON(": [");
   1.120 +        needComma_ = false;
   1.121 +    }
   1.122 +
   1.123 +    void endArray() {
   1.124 +        needComma_ = false;
   1.125 +        pJSON("]");
   1.126 +        needComma_ = true;
   1.127 +    }
   1.128 +
   1.129 +    jschar *finishJSString() {
   1.130 +        char *buf = finishCString();
   1.131 +        if (!buf)
   1.132 +            return nullptr;
   1.133 +
   1.134 +        size_t nchars = strlen(buf);
   1.135 +        jschar *out = js_pod_malloc<jschar>(nchars + 1);
   1.136 +        if (!out) {
   1.137 +            oom_ = true;
   1.138 +            js_free(buf);
   1.139 +            return nullptr;
   1.140 +        }
   1.141 +
   1.142 +        InflateStringToBuffer(buf, nchars, out);
   1.143 +        js_free(buf);
   1.144 +
   1.145 +        out[nchars] = 0;
   1.146 +        return out;
   1.147 +    }
   1.148 +
   1.149 +    char *finishCString() {
   1.150 +        if (oom_)
   1.151 +            return nullptr;
   1.152 +
   1.153 +        buf_.append('\0');
   1.154 +
   1.155 +        char *buf = buf_.extractRawBuffer();
   1.156 +        if (!buf)
   1.157 +            oom_ = true;
   1.158 +
   1.159 +        return buf;
   1.160 +    }
   1.161 +
   1.162 +  private:
   1.163 +    void append(const char *name, const char *vfmt,
   1.164 +                va_list va, const char *units)
   1.165 +    {
   1.166 +        char val[MaxFieldValueLength];
   1.167 +        JS_vsnprintf(val, MaxFieldValueLength, vfmt, va);
   1.168 +        put(name, val, units, false);
   1.169 +    }
   1.170 +
   1.171 +    void p(const char *cstr) {
   1.172 +        if (oom_)
   1.173 +            return;
   1.174 +
   1.175 +        if (!buf_.append(cstr, strlen(cstr)))
   1.176 +            oom_ = true;
   1.177 +    }
   1.178 +
   1.179 +    void p(const char c) {
   1.180 +        if (oom_)
   1.181 +            return;
   1.182 +
   1.183 +        if (!buf_.append(c))
   1.184 +            oom_ = true;
   1.185 +    }
   1.186 +
   1.187 +    void pJSON(const char *str) {
   1.188 +        if (asJSON_)
   1.189 +            p(str);
   1.190 +    }
   1.191 +
   1.192 +    void put(const char *name, const char *val, const char *units, bool valueIsQuoted) {
   1.193 +        if (needComma_)
   1.194 +            p(", ");
   1.195 +        needComma_ = true;
   1.196 +
   1.197 +        putKey(name);
   1.198 +        p(": ");
   1.199 +        if (valueIsQuoted)
   1.200 +            putQuoted(val);
   1.201 +        else
   1.202 +            p(val);
   1.203 +        if (!asJSON_)
   1.204 +            p(units);
   1.205 +    }
   1.206 +
   1.207 +    void putQuoted(const char *str) {
   1.208 +        pJSON("\"");
   1.209 +        p(str);
   1.210 +        pJSON("\"");
   1.211 +    }
   1.212 +
   1.213 +    void putKey(const char *str) {
   1.214 +        if (!asJSON_) {
   1.215 +            p(str);
   1.216 +            return;
   1.217 +        }
   1.218 +
   1.219 +        p("\"");
   1.220 +        const char *c = str;
   1.221 +        while (*c) {
   1.222 +            if (*c == ' ' || *c == '\t')
   1.223 +                p('_');
   1.224 +            else if (isupper(*c))
   1.225 +                p(tolower(*c));
   1.226 +            else if (*c == '+')
   1.227 +                p("added_");
   1.228 +            else if (*c == '-')
   1.229 +                p("removed_");
   1.230 +            else if (*c != '(' && *c != ')')
   1.231 +                p(*c);
   1.232 +            c++;
   1.233 +        }
   1.234 +        p("\"");
   1.235 +    }
   1.236 +};
   1.237 +
   1.238 +/*
   1.239 + * If this fails, then you can either delete this assertion and allow all
   1.240 + * larger-numbered reasons to pile up in the last telemetry bucket, or switch
   1.241 + * to GC_REASON_3 and bump the max value.
   1.242 + */
   1.243 +JS_STATIC_ASSERT(JS::gcreason::NUM_TELEMETRY_REASONS >= JS::gcreason::NUM_REASONS);
   1.244 +
   1.245 +const char *
   1.246 +js::gcstats::ExplainReason(JS::gcreason::Reason reason)
   1.247 +{
   1.248 +    switch (reason) {
   1.249 +#define SWITCH_REASON(name)                     \
   1.250 +        case JS::gcreason::name:                    \
   1.251 +          return #name;
   1.252 +        GCREASONS(SWITCH_REASON)
   1.253 +
   1.254 +        default:
   1.255 +          MOZ_ASSUME_UNREACHABLE("bad GC reason");
   1.256 +#undef SWITCH_REASON
   1.257 +    }
   1.258 +}
   1.259 +
   1.260 +static double
   1.261 +t(int64_t t)
   1.262 +{
   1.263 +    return double(t) / PRMJ_USEC_PER_MSEC;
   1.264 +}
   1.265 +
   1.266 +struct PhaseInfo
   1.267 +{
   1.268 +    Phase index;
   1.269 +    const char *name;
   1.270 +    Phase parent;
   1.271 +};
   1.272 +
   1.273 +static const Phase PHASE_NO_PARENT = PHASE_LIMIT;
   1.274 +
   1.275 +static const PhaseInfo phases[] = {
   1.276 +    { PHASE_GC_BEGIN, "Begin Callback", PHASE_NO_PARENT },
   1.277 +    { PHASE_WAIT_BACKGROUND_THREAD, "Wait Background Thread", PHASE_NO_PARENT },
   1.278 +    { PHASE_MARK_DISCARD_CODE, "Mark Discard Code", PHASE_NO_PARENT },
   1.279 +    { PHASE_PURGE, "Purge", PHASE_NO_PARENT },
   1.280 +    { PHASE_MARK, "Mark", PHASE_NO_PARENT },
   1.281 +    { PHASE_MARK_ROOTS, "Mark Roots", PHASE_MARK },
   1.282 +    { PHASE_MARK_DELAYED, "Mark Delayed", PHASE_MARK },
   1.283 +    { PHASE_SWEEP, "Sweep", PHASE_NO_PARENT },
   1.284 +    { PHASE_SWEEP_MARK, "Mark During Sweeping", PHASE_SWEEP },
   1.285 +    { PHASE_SWEEP_MARK_TYPES, "Mark Types During Sweeping", PHASE_SWEEP_MARK },
   1.286 +    { PHASE_SWEEP_MARK_INCOMING_BLACK, "Mark Incoming Black Pointers", PHASE_SWEEP_MARK },
   1.287 +    { PHASE_SWEEP_MARK_WEAK, "Mark Weak", PHASE_SWEEP_MARK },
   1.288 +    { PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK },
   1.289 +    { PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK },
   1.290 +    { PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK },
   1.291 +    { PHASE_FINALIZE_START, "Finalize Start Callback", PHASE_SWEEP },
   1.292 +    { PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP },
   1.293 +    { PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP },
   1.294 +    { PHASE_SWEEP_DISCARD_CODE, "Sweep Discard Code", PHASE_SWEEP_COMPARTMENTS },
   1.295 +    { PHASE_SWEEP_TABLES, "Sweep Tables", PHASE_SWEEP_COMPARTMENTS },
   1.296 +    { PHASE_SWEEP_TABLES_WRAPPER, "Sweep Cross Compartment Wrappers", PHASE_SWEEP_TABLES },
   1.297 +    { PHASE_SWEEP_TABLES_BASE_SHAPE, "Sweep Base Shapes", PHASE_SWEEP_TABLES },
   1.298 +    { PHASE_SWEEP_TABLES_INITIAL_SHAPE, "Sweep Initial Shapes", PHASE_SWEEP_TABLES },
   1.299 +    { PHASE_SWEEP_TABLES_TYPE_OBJECT, "Sweep Type Objects", PHASE_SWEEP_TABLES },
   1.300 +    { PHASE_SWEEP_TABLES_BREAKPOINT, "Sweep Breakpoints", PHASE_SWEEP_TABLES },
   1.301 +    { PHASE_SWEEP_TABLES_REGEXP, "Sweep Regexps", PHASE_SWEEP_TABLES },
   1.302 +    { PHASE_DISCARD_ANALYSIS, "Discard Analysis", PHASE_SWEEP_COMPARTMENTS },
   1.303 +    { PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS },
   1.304 +    { PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS },
   1.305 +    { PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
   1.306 +    { PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
   1.307 +    { PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
   1.308 +    { PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
   1.309 +    { PHASE_SWEEP_SHAPE, "Sweep Shape", PHASE_SWEEP },
   1.310 +    { PHASE_SWEEP_JITCODE, "Sweep JIT code", PHASE_SWEEP },
   1.311 +    { PHASE_FINALIZE_END, "Finalize End Callback", PHASE_SWEEP },
   1.312 +    { PHASE_DESTROY, "Deallocate", PHASE_SWEEP },
   1.313 +    { PHASE_GC_END, "End Callback", PHASE_NO_PARENT },
   1.314 +    { PHASE_LIMIT, nullptr, PHASE_NO_PARENT }
   1.315 +};
   1.316 +
   1.317 +static void
   1.318 +FormatPhaseTimes(StatisticsSerializer &ss, const char *name, int64_t *times)
   1.319 +{
   1.320 +    ss.beginObject(name);
   1.321 +    for (unsigned i = 0; phases[i].name; i++)
   1.322 +        ss.appendIfNonzeroMS(phases[i].name, t(times[phases[i].index]));
   1.323 +    ss.endObject();
   1.324 +}
   1.325 +
   1.326 +void
   1.327 +Statistics::gcDuration(int64_t *total, int64_t *maxPause)
   1.328 +{
   1.329 +    *total = *maxPause = 0;
   1.330 +    for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) {
   1.331 +        *total += slice->duration();
   1.332 +        if (slice->duration() > *maxPause)
   1.333 +            *maxPause = slice->duration();
   1.334 +    }
   1.335 +}
   1.336 +
   1.337 +void
   1.338 +Statistics::sccDurations(int64_t *total, int64_t *maxPause)
   1.339 +{
   1.340 +    *total = *maxPause = 0;
   1.341 +    for (size_t i = 0; i < sccTimes.length(); i++) {
   1.342 +        *total += sccTimes[i];
   1.343 +        *maxPause = Max(*maxPause, sccTimes[i]);
   1.344 +    }
   1.345 +}
   1.346 +
   1.347 +bool
   1.348 +Statistics::formatData(StatisticsSerializer &ss, uint64_t timestamp)
   1.349 +{
   1.350 +    int64_t total, longest;
   1.351 +    gcDuration(&total, &longest);
   1.352 +
   1.353 +    int64_t sccTotal, sccLongest;
   1.354 +    sccDurations(&sccTotal, &sccLongest);
   1.355 +
   1.356 +    double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
   1.357 +    double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
   1.358 +
   1.359 +    ss.beginObject(nullptr);
   1.360 +    if (ss.isJSON())
   1.361 +        ss.appendNumber("Timestamp", "%llu", "", (unsigned long long)timestamp);
   1.362 +    if (slices.length() > 1 || ss.isJSON())
   1.363 +        ss.appendDecimal("Max Pause", "ms", t(longest));
   1.364 +    else
   1.365 +        ss.appendString("Reason", ExplainReason(slices[0].reason));
   1.366 +    ss.appendDecimal("Total Time", "ms", t(total));
   1.367 +    ss.appendNumber("Zones Collected", "%d", "", collectedCount);
   1.368 +    ss.appendNumber("Total Zones", "%d", "", zoneCount);
   1.369 +    ss.appendNumber("Total Compartments", "%d", "", compartmentCount);
   1.370 +    ss.appendNumber("Minor GCs", "%d", "", counts[STAT_MINOR_GC]);
   1.371 +    ss.appendNumber("MMU (20ms)", "%d", "%", int(mmu20 * 100));
   1.372 +    ss.appendNumber("MMU (50ms)", "%d", "%", int(mmu50 * 100));
   1.373 +    ss.appendDecimal("SCC Sweep Total", "ms", t(sccTotal));
   1.374 +    ss.appendDecimal("SCC Sweep Max Pause", "ms", t(sccLongest));
   1.375 +    if (nonincrementalReason || ss.isJSON()) {
   1.376 +        ss.appendString("Nonincremental Reason",
   1.377 +                        nonincrementalReason ? nonincrementalReason : "none");
   1.378 +    }
   1.379 +    ss.appendNumber("Allocated", "%u", "MB", unsigned(preBytes / 1024 / 1024));
   1.380 +    ss.appendNumber("+Chunks", "%d", "", counts[STAT_NEW_CHUNK]);
   1.381 +    ss.appendNumber("-Chunks", "%d", "", counts[STAT_DESTROY_CHUNK]);
   1.382 +    ss.endLine();
   1.383 +
   1.384 +    if (slices.length() > 1 || ss.isJSON()) {
   1.385 +        ss.beginArray("Slices");
   1.386 +        for (size_t i = 0; i < slices.length(); i++) {
   1.387 +            int64_t width = slices[i].duration();
   1.388 +            if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME &&
   1.389 +                !slices[i].resetReason && !ss.isJSON())
   1.390 +            {
   1.391 +                continue;
   1.392 +            }
   1.393 +
   1.394 +            ss.beginObject(nullptr);
   1.395 +            ss.extra("    ");
   1.396 +            ss.appendNumber("Slice", "%d", "", i);
   1.397 +            ss.appendDecimal("Pause", "", t(width));
   1.398 +            ss.extra(" (");
   1.399 +            ss.appendDecimal("When", "ms", t(slices[i].start - slices[0].start));
   1.400 +            ss.appendString("Reason", ExplainReason(slices[i].reason));
   1.401 +            if (ss.isJSON()) {
   1.402 +                ss.appendDecimal("Page Faults", "",
   1.403 +                                 double(slices[i].endFaults - slices[i].startFaults));
   1.404 +
   1.405 +                ss.appendNumber("Start Timestamp", "%llu", "", (unsigned long long)slices[i].start);
   1.406 +                ss.appendNumber("End Timestamp", "%llu", "", (unsigned long long)slices[i].end);
   1.407 +            }
   1.408 +            if (slices[i].resetReason)
   1.409 +                ss.appendString("Reset", slices[i].resetReason);
   1.410 +            ss.extra("): ");
   1.411 +            FormatPhaseTimes(ss, "Times", slices[i].phaseTimes);
   1.412 +            ss.endLine();
   1.413 +            ss.endObject();
   1.414 +        }
   1.415 +        ss.endArray();
   1.416 +    }
   1.417 +    ss.extra("    Totals: ");
   1.418 +    FormatPhaseTimes(ss, "Totals", phaseTimes);
   1.419 +    ss.endObject();
   1.420 +
   1.421 +    return !ss.isOOM();
   1.422 +}
   1.423 +
   1.424 +jschar *
   1.425 +Statistics::formatMessage()
   1.426 +{
   1.427 +    StatisticsSerializer ss(StatisticsSerializer::AsText);
   1.428 +    formatData(ss, 0);
   1.429 +    return ss.finishJSString();
   1.430 +}
   1.431 +
   1.432 +jschar *
   1.433 +Statistics::formatJSON(uint64_t timestamp)
   1.434 +{
   1.435 +    StatisticsSerializer ss(StatisticsSerializer::AsJSON);
   1.436 +    formatData(ss, timestamp);
   1.437 +    return ss.finishJSString();
   1.438 +}
   1.439 +
   1.440 +Statistics::Statistics(JSRuntime *rt)
   1.441 +  : runtime(rt),
   1.442 +    startupTime(PRMJ_Now()),
   1.443 +    fp(nullptr),
   1.444 +    fullFormat(false),
   1.445 +    gcDepth(0),
   1.446 +    collectedCount(0),
   1.447 +    zoneCount(0),
   1.448 +    compartmentCount(0),
   1.449 +    nonincrementalReason(nullptr),
   1.450 +    preBytes(0),
   1.451 +    phaseNestingDepth(0)
   1.452 +{
   1.453 +    PodArrayZero(phaseTotals);
   1.454 +    PodArrayZero(counts);
   1.455 +
   1.456 +    char *env = getenv("MOZ_GCTIMER");
   1.457 +    if (!env || strcmp(env, "none") == 0) {
   1.458 +        fp = nullptr;
   1.459 +        return;
   1.460 +    }
   1.461 +
   1.462 +    if (strcmp(env, "stdout") == 0) {
   1.463 +        fullFormat = false;
   1.464 +        fp = stdout;
   1.465 +    } else if (strcmp(env, "stderr") == 0) {
   1.466 +        fullFormat = false;
   1.467 +        fp = stderr;
   1.468 +    } else {
   1.469 +        fullFormat = true;
   1.470 +
   1.471 +        fp = fopen(env, "a");
   1.472 +        JS_ASSERT(fp);
   1.473 +    }
   1.474 +}
   1.475 +
   1.476 +Statistics::~Statistics()
   1.477 +{
   1.478 +    if (fp) {
   1.479 +        if (fullFormat) {
   1.480 +            StatisticsSerializer ss(StatisticsSerializer::AsText);
   1.481 +            FormatPhaseTimes(ss, "", phaseTotals);
   1.482 +            char *msg = ss.finishCString();
   1.483 +            if (msg) {
   1.484 +                fprintf(fp, "TOTALS\n%s\n\n-------\n", msg);
   1.485 +                js_free(msg);
   1.486 +            }
   1.487 +        }
   1.488 +
   1.489 +        if (fp != stdout && fp != stderr)
   1.490 +            fclose(fp);
   1.491 +    }
   1.492 +}
   1.493 +
   1.494 +void
   1.495 +Statistics::printStats()
   1.496 +{
   1.497 +    if (fullFormat) {
   1.498 +        StatisticsSerializer ss(StatisticsSerializer::AsText);
   1.499 +        formatData(ss, 0);
   1.500 +        char *msg = ss.finishCString();
   1.501 +        if (msg) {
   1.502 +            fprintf(fp, "GC(T+%.3fs) %s\n", t(slices[0].start - startupTime) / 1000.0, msg);
   1.503 +            js_free(msg);
   1.504 +        }
   1.505 +    } else {
   1.506 +        int64_t total, longest;
   1.507 +        gcDuration(&total, &longest);
   1.508 +
   1.509 +        fprintf(fp, "%f %f %f\n",
   1.510 +                t(total),
   1.511 +                t(phaseTimes[PHASE_MARK]),
   1.512 +                t(phaseTimes[PHASE_SWEEP]));
   1.513 +    }
   1.514 +    fflush(fp);
   1.515 +}
   1.516 +
   1.517 +void
   1.518 +Statistics::beginGC()
   1.519 +{
   1.520 +    PodArrayZero(phaseStartTimes);
   1.521 +    PodArrayZero(phaseTimes);
   1.522 +
   1.523 +    slices.clearAndFree();
   1.524 +    sccTimes.clearAndFree();
   1.525 +    nonincrementalReason = nullptr;
   1.526 +
   1.527 +    preBytes = runtime->gcBytes;
   1.528 +}
   1.529 +
   1.530 +void
   1.531 +Statistics::endGC()
   1.532 +{
   1.533 +    crash::SnapshotGCStack();
   1.534 +
   1.535 +    for (int i = 0; i < PHASE_LIMIT; i++)
   1.536 +        phaseTotals[i] += phaseTimes[i];
   1.537 +
   1.538 +    if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
   1.539 +        int64_t total, longest;
   1.540 +        gcDuration(&total, &longest);
   1.541 +
   1.542 +        int64_t sccTotal, sccLongest;
   1.543 +        sccDurations(&sccTotal, &sccLongest);
   1.544 +
   1.545 +        (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, collectedCount == zoneCount ? 0 : 1);
   1.546 +        (*cb)(JS_TELEMETRY_GC_MS, t(total));
   1.547 +        (*cb)(JS_TELEMETRY_GC_MAX_PAUSE_MS, t(longest));
   1.548 +        (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK]));
   1.549 +        (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP]));
   1.550 +        (*cb)(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(phaseTimes[PHASE_MARK_ROOTS]));
   1.551 +        (*cb)(JS_TELEMETRY_GC_MARK_GRAY_MS, t(phaseTimes[PHASE_SWEEP_MARK_GRAY]));
   1.552 +        (*cb)(JS_TELEMETRY_GC_NON_INCREMENTAL, !!nonincrementalReason);
   1.553 +        (*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
   1.554 +        (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
   1.555 +        (*cb)(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
   1.556 +
   1.557 +        double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
   1.558 +        (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
   1.559 +    }
   1.560 +
   1.561 +    if (fp)
   1.562 +        printStats();
   1.563 +}
   1.564 +
   1.565 +void
   1.566 +Statistics::beginSlice(int collectedCount, int zoneCount, int compartmentCount,
   1.567 +                       JS::gcreason::Reason reason)
   1.568 +{
   1.569 +    this->collectedCount = collectedCount;
   1.570 +    this->zoneCount = zoneCount;
   1.571 +    this->compartmentCount = compartmentCount;
   1.572 +
   1.573 +    bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
   1.574 +    if (first)
   1.575 +        beginGC();
   1.576 +
   1.577 +    SliceData data(reason, PRMJ_Now(), gc::GetPageFaultCount());
   1.578 +    (void) slices.append(data); /* Ignore any OOMs here. */
   1.579 +
   1.580 +    if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
   1.581 +        (*cb)(JS_TELEMETRY_GC_REASON, reason);
   1.582 +
   1.583 +    // Slice callbacks should only fire for the outermost level
   1.584 +    if (++gcDepth == 1) {
   1.585 +        bool wasFullGC = collectedCount == zoneCount;
   1.586 +        if (JS::GCSliceCallback cb = runtime->gcSliceCallback)
   1.587 +            (*cb)(runtime, first ? JS::GC_CYCLE_BEGIN : JS::GC_SLICE_BEGIN,
   1.588 +                  JS::GCDescription(!wasFullGC));
   1.589 +    }
   1.590 +}
   1.591 +
   1.592 +void
   1.593 +Statistics::endSlice()
   1.594 +{
   1.595 +    slices.back().end = PRMJ_Now();
   1.596 +    slices.back().endFaults = gc::GetPageFaultCount();
   1.597 +
   1.598 +    if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
   1.599 +        (*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
   1.600 +        (*cb)(JS_TELEMETRY_GC_RESET, !!slices.back().resetReason);
   1.601 +    }
   1.602 +
   1.603 +    bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
   1.604 +    if (last)
   1.605 +        endGC();
   1.606 +
   1.607 +    // Slice callbacks should only fire for the outermost level
   1.608 +    if (--gcDepth == 0) {
   1.609 +        bool wasFullGC = collectedCount == zoneCount;
   1.610 +        if (JS::GCSliceCallback cb = runtime->gcSliceCallback)
   1.611 +            (*cb)(runtime, last ? JS::GC_CYCLE_END : JS::GC_SLICE_END,
   1.612 +                  JS::GCDescription(!wasFullGC));
   1.613 +    }
   1.614 +
   1.615 +    /* Do this after the slice callback since it uses these values. */
   1.616 +    if (last)
   1.617 +        PodArrayZero(counts);
   1.618 +}
   1.619 +
   1.620 +void
   1.621 +Statistics::beginPhase(Phase phase)
   1.622 +{
   1.623 +    /* Guard against re-entry */
   1.624 +    JS_ASSERT(!phaseStartTimes[phase]);
   1.625 +
   1.626 +#ifdef DEBUG
   1.627 +    JS_ASSERT(phases[phase].index == phase);
   1.628 +    Phase parent = phaseNestingDepth ? phaseNesting[phaseNestingDepth - 1] : PHASE_NO_PARENT;
   1.629 +    JS_ASSERT(phaseNestingDepth < MAX_NESTING);
   1.630 +    JS_ASSERT_IF(gcDepth == 1, phases[phase].parent == parent);
   1.631 +    phaseNesting[phaseNestingDepth] = phase;
   1.632 +    phaseNestingDepth++;
   1.633 +#endif
   1.634 +
   1.635 +    phaseStartTimes[phase] = PRMJ_Now();
   1.636 +}
   1.637 +
   1.638 +void
   1.639 +Statistics::endPhase(Phase phase)
   1.640 +{
   1.641 +    phaseNestingDepth--;
   1.642 +
   1.643 +    int64_t t = PRMJ_Now() - phaseStartTimes[phase];
   1.644 +    slices.back().phaseTimes[phase] += t;
   1.645 +    phaseTimes[phase] += t;
   1.646 +    phaseStartTimes[phase] = 0;
   1.647 +}
   1.648 +
   1.649 +int64_t
   1.650 +Statistics::beginSCC()
   1.651 +{
   1.652 +    return PRMJ_Now();
   1.653 +}
   1.654 +
   1.655 +void
   1.656 +Statistics::endSCC(unsigned scc, int64_t start)
   1.657 +{
   1.658 +    if (scc >= sccTimes.length() && !sccTimes.resize(scc + 1))
   1.659 +        return;
   1.660 +
   1.661 +    sccTimes[scc] += PRMJ_Now() - start;
   1.662 +}
   1.663 +
   1.664 +/*
   1.665 + * MMU (minimum mutator utilization) is a measure of how much garbage collection
   1.666 + * is affecting the responsiveness of the system. MMU measurements are given
   1.667 + * with respect to a certain window size. If we report MMU(50ms) = 80%, then
   1.668 + * that means that, for any 50ms window of time, at least 80% of the window is
   1.669 + * devoted to the mutator. In other words, the GC is running for at most 20% of
   1.670 + * the window, or 10ms. The GC can run multiple slices during the 50ms window
   1.671 + * as long as the total time it spends is at most 10ms.
   1.672 + */
   1.673 +double
   1.674 +Statistics::computeMMU(int64_t window)
   1.675 +{
   1.676 +    JS_ASSERT(!slices.empty());
   1.677 +
   1.678 +    int64_t gc = slices[0].end - slices[0].start;
   1.679 +    int64_t gcMax = gc;
   1.680 +
   1.681 +    if (gc >= window)
   1.682 +        return 0.0;
   1.683 +
   1.684 +    int startIndex = 0;
   1.685 +    for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) {
   1.686 +        gc += slices[endIndex].end - slices[endIndex].start;
   1.687 +
   1.688 +        while (slices[endIndex].end - slices[startIndex].end >= window) {
   1.689 +            gc -= slices[startIndex].end - slices[startIndex].start;
   1.690 +            startIndex++;
   1.691 +        }
   1.692 +
   1.693 +        int64_t cur = gc;
   1.694 +        if (slices[endIndex].end - slices[startIndex].start > window)
   1.695 +            cur -= (slices[endIndex].end - slices[startIndex].start - window);
   1.696 +        if (cur > gcMax)
   1.697 +            gcMax = cur;
   1.698 +    }
   1.699 +
   1.700 +    return double(window - gcMax) / window;
   1.701 +}

mercurial